Passing uint array to Dll to be populated (Nvidia NVML library) - c#

I'm attempting to pass a uint array into the NVML function nvmlDeviceGetAccountingPids(Doc here) from C#, here's a minimum working sample of what I have so far:
{
public const string NVMLDLL = "nvml.dll";
public static class Constants
{
public const int NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE = 16;
public const int NVML_DEVICE_NAME_BUFFER_SIZE = 64;
public const int NVML_DEVICE_NAME_V2_BUFFER_SIZE = 96;
public const int NVML_DEVICE_PART_NUMBER_BUFFER_SIZE = 80;
public const int NVML_DEVICE_SERIAL_BUFFER_SIZE = 30;
public const int NVML_DEVICE_UUID_BUFFER_SIZE = 80;
public const int NVML_DEVICE_UUID_V2_BUFFER_SIZE = 96;
public const int NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE = 32;
public const int NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE = 80;
public const int NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE = 80;
public const int NVML_INIT_FLAG_NO_ATTACH = 2;
public const int NVML_INIT_FLAG_NO_GPUS = 1;
}
public enum nvmlReturn_t
{
NVML_SUCCESS = 0, // The operation was successful.
NVML_ERROR_UNINITIALIZED = 1, // NVML was not first initialized with nvmlInit().
NVML_ERROR_INVALID_ARGUMENT = 2, // A supplied argument is invalid.
NVML_ERROR_NOT_SUPPORTED = 3, // The requested operation is not available on target device.
NVML_ERROR_NO_PERMISSION = 4, // The current user does not have permission for operation.
NVML_ERROR_ALREADY_INITIALIZED = 5, // Deprecated: Multiple initializations are now allowed through ref counting.
NVML_ERROR_NOT_FOUND = 6, // A query to find an object was unsuccessful.
NVML_ERROR_INSUFFICIENT_SIZE = 7, // An input argument is not large enough.
NVML_ERROR_INSUFFICIENT_POWER = 8, // A device's external power cables are not properly attached.
NVML_ERROR_DRIVER_NOT_LOADED = 9, // NVIDIA driver is not loaded.
NVML_ERROR_TIMEOUT = 10, // User provided timeout passed.
NVML_ERROR_IRQ_ISSUE = 11, // NVIDIA Kernel detected an interrupt issue with a GPU.
NVML_ERROR_LIBRARY_NOT_FOUND = 12, // NVML Shared Library couldn't be found or loaded.
NVML_ERROR_FUNCTION_NOT_FOUND = 13, // Local version of NVML doesn't implement this function.
NVML_ERROR_CORRUPTED_INFOROM = 14, // infoROM is corrupted
NVML_ERROR_GPU_IS_LOST = 15, // The GPU has fallen off the bus or has otherwise become inaccessible.
NVML_ERROR_RESET_REQUIRED = 16, // The GPU requires a reset before it can be used again.
NVML_ERROR_OPERATING_SYSTEM = 17, // The GPU control device has been blocked by the operating system/cgroups.
NVML_ERROR_LIB_RM_VERSION_MISMATCH = 18, // RM detects a driver/library version mismatch.
NVML_ERROR_IN_USE = 19, // An operation cannot be performed because the GPU is currently in use.
NVML_ERROR_MEMORY = 20, // Insufficient memory.
NVML_ERROR_NO_DATA = 21, // No data.
NVML_ERROR_VGPU_ECC_NOT_SUPPORTED = 22, // The requested vgpu operation is not available on target device, becasue ECC is enabled.
NVML_ERROR_INSUFFICIENT_RESOURCES = 23, // Ran out of critical resources, other than memory.
NVML_ERROR_FREQ_NOT_SUPPORTED = 24, // Ran out of critical resources, other than memory.
NVML_ERROR_UNKNOWN = 999 // An internal driver error occurred.
}
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlInit_v2();
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlShutdown();
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetCount_v2(out uint deviceCount);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetHandleByIndex_v2(int index, out IntPtr handle);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetName(IntPtr device, [MarshalAs(UnmanagedType.LPStr)] StringBuilder deviceName, uint length);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetAccountingPids(IntPtr device, ref uint count, [In, Out] uint[] pids);
static void Main(string[] args)
{
if (nvmlInit_v2() == nvmlReturn_t.NVML_SUCCESS)
{
nvmlDeviceGetCount_v2(out uint deviceCount);
for (int i = 0; i < deviceCount; i++)
{
StringBuilder name = new();
nvmlDeviceGetHandleByIndex_v2(i, out IntPtr _device);
nvmlDeviceGetName(_device, name, Constants.NVML_DEVICE_NAME_V2_BUFFER_SIZE);
// arbitrary number for now
uint count = 5;
uint[] pids = new uint[count];
nvmlDeviceGetAccountingPids(_device, ref count, pids);
foreach(uint ui in pids) Console.WriteLine(ui);
}
nvmlShutdown();
}
}
}
and so far it's returning the correct # of processes that are utilizing the gpu, but the PID array keeps returning all 0's. I'm not particularly experienced with C# so if someone could show me the proper syntax for this(or lemme know if I have to resort to IntPtr's), the original function has this signature:
nvmlReturn_t nvmlDeviceGetAccountingPids ( nvmlDevice_t device, unsigned int* count, unsigned int* pids )
Parameters
device
The identifier of the target device
count
Reference in which to provide the pids array size, and to return the number of elements ready to be queried
pids
Reference in which to return list of process ids
If anyone knows that magic combo I'd sure appreciate it, thank you for your time =)

Sometimes I think I only type things to work them out ^_^
static void Main(string[] args)
{
if (nvmlInit_v2() == nvmlReturn_t.NVML_SUCCESS)
{
nvmlDeviceGetCount_v2(out uint deviceCount);
for (int i = 0; i < deviceCount; i++)
{
StringBuilder name = new();
nvmlDeviceGetHandleByIndex_v2(i, out IntPtr _device);
nvmlDeviceGetName(_device, name, Constants.NVML_DEVICE_NAME_V2_BUFFER_SIZE);
// arbitrary number for now
uint count = 0;
nvmlDeviceGetAccountingPids(_device, ref count, null);
uint[] pids = new uint[count];
if (nvmlDeviceGetAccountingPids(_device, ref count, pids) == nvmlReturn_t.NVML_SUCCESS)
{
foreach (uint ui in pids) Console.WriteLine(ui);
}
}
nvmlShutdown();
}
so it turns out that when passing the count to the function it wants the same number as the size of the array in order to populate the pids list(which makes sense I suppose). This is retrieved by just setting count to "0" and passing a null array to the function and it will set count to the size of the array. Create a new uint array with that size, pass the array/count and BAM, it returns the pids as desired.
Thanks for reading SO!
If anyone has some constructive criticism in terms of syntax/practices please do comment so I can improve this code as I go. Have a wonderful day πŸ‘

Related

Get Value from Console Application inside C# Variable [duplicate]

I need to read text from a particular location in the console, say 5,5.
If I were to need to write to this location, it would simply be:
Console.SetCursorPosition(5, 5);
Console.Write("My text");
Is there any way i can read in a similar way?
Just to clarify:
I don't want to stop to take an input from the user, there's a chance even that the input won't be from the user, but something previously printed out. I literally want some sort of:
Console.GetCharAtLocation(5,5) or something similar.
Here is a C# code utility that can read what's currently in the Console buffer (not the window, the buffer):
Sample usage:
class Program
{
static void Main(string[] args)
{
// read 10 lines from the top of the console buffer
foreach (string line in ConsoleReader.ReadFromBuffer(0, 0, (short)Console.BufferWidth, 10))
{
Console.Write(line);
}
}
}
Utility:
public class ConsoleReader
{
public static IEnumerable<string> ReadFromBuffer(short x, short y, short width, short height)
{
IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
if (buffer == null)
throw new OutOfMemoryException();
try
{
COORD coord = new COORD();
SMALL_RECT rc = new SMALL_RECT();
rc.Left = x;
rc.Top = y;
rc.Right = (short)(x + width - 1);
rc.Bottom = (short)(y + height - 1);
COORD size = new COORD();
size.X = width;
size.Y = height;
const int STD_OUTPUT_HANDLE = -11;
if (!ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), buffer, size, coord, ref rc))
{
// 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr ptr = buffer;
for (int h = 0; h < height; h++)
{
StringBuilder sb = new StringBuilder();
for (int w = 0; w < width; w++)
{
CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
char[] chars = Console.OutputEncoding.GetChars(ci.charData);
sb.Append(chars[0]);
ptr += Marshal.SizeOf(typeof(CHAR_INFO));
}
yield return sb.ToString();
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] charData;
public short attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
}
A simplified demo that works in Windows 10 for reading a single character from the specified (X,Β Y) position on the screen. Tested with .NET 4.7.2.
First, here's a line of code which populates the Console with a demo grid. Note that it should render in the top left corner of your screen in order for the demo to work.
static void Populate_Console()
{
Console.Clear();
Console.Write(#"
β”Œβ”€β”€β”€β”€β”€β”€β”€β”
1β”‚C D E Fβ”‚
2β”‚G H I Jβ”‚
3β”‚K L M Nβ”‚
4β”‚O P Q Rβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜
2 4 6 8 ".Trim());
}
It should look like this:
Now let's read some characters back. To start you'll need the native console handle for stdout. Here's the P/Invoke method for getting it from Win32:
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr GetStdHandle(int num);
Now for the cool part; this seems to be the only answer on this page so far which uses the ReadConsoleOutputCharacter Win32 function. Although it doesn't let you get the character color attributes, this approach does save all the trouble of dealing with copy rectangles and having to use CreateConsoleScreenBuffer to allocate screen buffers and copy between them.
There are separate Ansi and Unicode versions, and you need to call the proper one depending on the code page that's active in the Console window. I show both P/Invoke signatures here, but for simplicity, in the example I'll just continue with the Ansi version:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.Bool)] // Μ²β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€^
static extern bool ReadConsoleOutputCharacterA(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out byte ch, // AΜ²NΜ²SΜ²IΜ² character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)] // Μ²β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€^
static extern bool ReadConsoleOutputCharacterW(
IntPtr hStdout, // result of 'GetStdHandle(-11)'
out Char ch, // UΜ²nΜ²iΜ²cΜ²oΜ²dΜ²eΜ² character result
uint c_in, // (set to '1')
uint coord_XY, // screen location to read, X:loword, Y:hiword
out uint c_out); // (unwanted, discard)
You may notice I've stripped down the marshaling on these to the bare minimum needed for the purposes of my example code, which is designed to only fetch one character at a time. Therefore, you will probably find that c_in must always be 1, due to the managed pointer declarations β€˜out byte ch’ and β€˜out Char ch’.
That's really all you need; calling the appropriate P/Invoke function as described above is mostly self-explanatory if you limit yourself to reading a single character. To show this with a trivial example, I'll finish with the cute demo program, that reads four characters back from the Console, along a diagonal of the grid we drew above.
static void Windows_Console_Readback()
{
var stdout = GetStdHandle(-11);
for (uint coord, y = 1; y <= 4; y++)
{
coord = (5 - y) * 2; // loword <-- X coord to read
coord |= y << 16; // hiword <-- Y coord to read
if (!ReadConsoleOutputCharacterA(
stdout,
out byte chAnsi, // result: single ANSI char
1, // # of chars to read
coord, // (X,Y) screen location to read (see above)
out _)) // result: actual # of chars (unwanted)
throw new Win32Exception();
Console.Write(" " + (Char)chAnsi + " ");
}
}
And there you have it...
This functionality doesn't exist. It's theoretically possible for you to override the input and output streams on the console to keep your own copy of the console buffer that you could read from, but it would be non-trivial (and probably couldn't support all of the edge cases such as an external program hooking into your console and reading/writing to it).
Forget about it, too much trouble, you could read from buffer and get all the current console output, but that would be too much.
My suggestion is to create a ConsoleWriter delegation, you choose how, could be a class or just an static method, and this writer would keep the last line in a property so each time you would Console.WriteLine something you just call your delegation, with your implementation and at the end it calls Console.WriteLine.
As #Servy has stated, there isn't any built-in functionality (that I know of, or can find) that can do what you want. However, there is a work-around (it's a bit of a hack, but it worked).
You can create your own buffer in memory, or on disk. Whenever you output to the Console, also output to your buffer. You can then use your buffer to read from in ways that you couldn't with the Console.
There are two ways to buffer: on disk or in memory. You can use the Console.BufferWidth and Console.BufferHeight properties to find out your buffer size. I found it simpler to do this in memory using an array of strings (each string was a line of output, and there were a number of strings in the array equal to the BufferHeight, if I remember correctly). A colleague ended up doing the same thing on disk.
You'll want to create a method to replace Console.Write and Console.WriteLine, so that you can write to both buffers at once. Something like:
public void MyWrite( string output ) {
Console.Write( output );
Array.Write( output ); // obvious pseudo-code
}
I found it helpful to wrap a Class around the array and implement methods to support it ... you could then implement your GetCharAtLocation( int i, int j ) method, as well as any other functionality you need there.
What about:
class Program {
static void Main( string[ ] args ) {
CustomizedConsole.WriteLine( "Lorem Ipsum" ); //Lorem Ipsum
Console.WriteLine( CustomizedConsole.ReadContent( 6, 5 ) ); //Ipsum
Console.WriteLine( CustomizedConsole.GetCharAtLocation( 0, 0 ) ); //L
}
}
static class CustomizedConsole {
private static List<char> buffer = new List<char>();
private static int lineCharCount = 0;
public static void Write(string s){
lineCharCount += s.Length;
buffer.AddRange( s );
Console.Write( s );
}
public static void WriteLine(string s ) {
for ( int i = 0; i < Console.BufferWidth - lineCharCount - s.Length; i++ )
s += " ";
buffer.AddRange( s );
Console.WriteLine( s );
lineCharCount = 0;
}
public static string ReadContent( int index, int count ) {
return new String(buffer.Skip( index ).Take( count ).ToArray());
}
public static char GetCharAtLocation( int x, int y ) {
return buffer[ Console.BufferHeight * x + y ];
}
}
EDIT :
As said the others this is just a trivial case where there are a lot of other things to improve. But I wrote this only as a starting point.

my c++ and c# interop crashes in 64 bit, why? pointer size?

i got a native 32 bit dll (no source) which runs as a plugin in an application i use. I've done another native dll myself which will communicate with that plugin in order to create and update the plugin's controls.
From that dll i've exported the functions I need in order to control the plugin from my c# application (with p/invoke).
here's the code:
h file:
#pragma once
#include "include\SpoutControls.h"
extern "C" { __declspec(dllexport) void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text); }
extern "C" { __declspec(dllexport) bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls); }
extern "C" { __declspec(dllexport) void CloseControls(); }
//
extern "C" __declspec(dllexport) int ReleaseMemory(float *pArray)
{
delete[] pArray;
//delete[] Usize;
return 0;
};
the cpp:
#include "SpoutControls4vvvv.h"
//SpoutControls and the functions
//CreateControl, OpenControls, CheckControls, CloseControls
//are declared in SpoutControls.h, which comes with the 32 bit plugin dll
SpoutControls spoutcontrols;
void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v=0, t=0, p=0, s = 0;
for (int controlID = 0; controlID < all; controlID++) {
if (types[controlID] == 0) {
spoutcontrols.CreateControl(names[controlID], "float",0.0,1.0, floats[v]);
v++;
}
if (types[controlID] == 1) {
spoutcontrols.CreateControl(names[controlID], "bool", toggles[t]);
t++;
}
if (types[controlID] == 2) {
spoutcontrols.CreateControl(names[controlID], "event", press[p]);
p++;
}
if (types[controlID] == 3) {
spoutcontrols.CreateControl(names[controlID], "text", text[s]);
s++;
}
}
spoutcontrols.OpenControls(sendername);
}
bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v = 0, t = 0, p = 0, s = 0;
if (spoutcontrols.CheckControls(myControls)) {
for (int controlID = 0; controlID < all; controlID++) {
if (myControls[controlID].type == 10) {
floats[v] = myControls[controlID].value;
v++;
}
if (myControls[controlID].type == 0) {
toggles[t] = myControls[controlID].value;
t++;
}
if (myControls[controlID].type == 1) {
press[p] = myControls[controlID].value;
p++;
}
if (myControls[controlID].type == 100) {
text[s] = myControls[controlID].text.data();
s++;
}
}
return true;
}
return false;
}
void CloseControls() {
spoutcontrols.CloseControls();
}
and here's the c# code:
public unsafe class SystemSpoutSenderNode: IDisposable
{
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern void InitializeControls(IntPtr sendername, IntPtr numControls,String[] names, IntPtr types, IntPtr floats, IntPtr toggles, IntPtr press, String[] text);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int CloseControls();
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool UpdateControls([In, Out] String[] text, [In, Out] float[] floats, [In, Out] float[] toggles, [In, Out] float[] press, IntPtr numControls);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int ReleaseMemory(IntPtr ptr);
public void Evaluate(int SpreadMax)
{
//countControls determines number of controls per type (string,float,toggle,click)
int[] controls = countControls(FType);
//sumControls will just add up all elements in controls
int all = sumControls(controls);
//in my code these arrays will get filled with values, deleted here for readability
String[] names = new String[all];
int[] types = new int[all];
float[] floats = new float[controls[0]];
float[] toggles = new float[controls[1]];
float[] press = new float[controls[2]];
String[] text = new String[controls[3]];
//initialze return arrays
String[] Rtext = new String[controls[3]];
float[] Rfloats = new float[controls[0]];
float[] Rtoggles = new float[controls[1]];
float[] Rpress = new float[controls[2]];
//allocate pointers
IntPtr SndrNamePtr = NativeUtf8FromString(FSenderName);
IntPtr BinPtr = Marshal.AllocHGlobal(4*sizeof(int));
IntPtr TypePtr = Marshal.AllocHGlobal(all*sizeof(int));
IntPtr FloatPtr = Marshal.AllocHGlobal(controls[0]*sizeof(float));
IntPtr TogglePtr = Marshal.AllocHGlobal(controls[1]*sizeof(float));
IntPtr PressPtr = Marshal.AllocHGlobal(controls[2]*sizeof(float));
try
{
//copy control info + defaults to pointer
Marshal.Copy(controls, 0, BinPtr, 4);
Marshal.Copy(types, 0, TypePtr, all);
Marshal.Copy(floats, 0, FloatPtr, controls[0]);
Marshal.Copy(toggles, 0, TogglePtr, controls[1]);
Marshal.Copy(press, 0, PressPtr, controls[2]);
//initialize controls
if (FWrite) InitializeControls(SndrNamePtr,BinPtr,names,TypePtr,FloatPtr,TogglePtr,PressPtr,text);
//update controls
bool changed = UpdateControls(Rtext,Rfloats,Rtoggles,Rpress,BinPtr);
//FF, FT, FS and FP are the outputs in my c# host
if (changed){
for(int j=0; j<controls[0];j++){
FF[j]=Rfloats[j];
}
for(int j=0; j<controls[1];j++){
FT[j]=FloatToBool(Rtoggles[j]);
}
for(int j=0; j<controls[3];j++){
FS[j]=Rtext[j];
}
}
for(int j=0; j<controls[2];j++){
FP[j]=FloatToBool(Rpress[j]);
}
}
finally
{
Marshal.FreeHGlobal(SndrNamePtr);
Marshal.FreeHGlobal(BinPtr);
Marshal.FreeHGlobal(FloatPtr);
Marshal.FreeHGlobal(TogglePtr);
Marshal.FreeHGlobal(PressPtr);
}
}
}
}
public void Dispose()
{
CleanUp();
CloseControls();
}
}
NOTE: the c# code runs without precompiling in a frame-based, c# host environment for graphical programming (vvvv), therefore i've deleted host specific decalarations of inputs (FType,FSenderName) and outputs (FF,FS,FP,FT) to avoid confusion. These will be used to "connect" this code with other functionality. Evaluate will be called every frame by the host.
Now to the actual question(s):
it's working fine so far in 32 bit, but in 64 bit my c# host crashes without any message. after some reading i believe this is due to pointer sizes being different in 32/64bit systems, but i'm not exactly sure what to do/if this actually applies here. I would be very thankful if you could
explain me how (and why) to get this code to run in 64 bit
point out any other mistakes you might spot along the way- i'm completely new to c++ and still a beginner in c#, so i'm pretty confident there's a lot to improve here; especially: memory leaks and passing the values from c++ to c# and vice versa...uiuiui.
I've understood that I shouldn't cast a pointer to an int in 64 bit, so the last thing I've tried is to change from
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
to
int Vcontrols = (INT_PTR)numControls[0];
int Tcontrols = (INT_PTR)numControls[1];
int Pcontrols = (INT_PTR)numControls[2];
int Scontrols = (INT_PTR)numControls[3];
but with no luck, therefore I'm posting my original problem, even if this is a correct improvement(?).
EDIT: thanks to #dkackman for pointing out one unclear point: my cpp code calls functions which come as source code (SpoutControls.h) with the native 32 bit dll. It's not the source for the 32 bit dll itself but declares the functions used to (as far as i can tell) access the same shared memory as the 32 bit dll.
I can also copy paste the code here if this might be the problem?
Also can be found here
thank you.
I am afraid you are out of luck. If your process is 64bit, you won't be able to load that 32bit dll, no matter how much you try.
Can I load a 32 bit DLL into a 64 bit process on Windows?
from https://msdn.microsoft.com/en-us/library/windows/desktop/aa384231(v=vs.85).aspx
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link
library (DLL).
Without access to its source, your only option would be to convert your host to 32bit or otherwise figure out how to host the 32bit plugin in a 32bit process and use some sort of IPC to communicate with it from a 64bit host process.
So my guess is that this has nothing to do with your wrapper, array passing or interop code.

Enumerating Network Sessions

I wanted to pull data about the connected network users from the Computer Management -> Shared Folders -> Sessions tab into my c# application. Can anybody guide me on what namespaces have to be used along with some sample code to import username and ip address from Computer Management -> Shared Folders -> Sessions tab?
Regards
You want NetSessionEnum, which:
Provides information about sessions established on a server.
When passed a level of 502, it'll return an array of:
...the name of the computer; name of the user; open files, pipes, and devices on the computer; and the name of the transport the client is using.
Luckily for you, pinvoke.net has the necessary signatures and even some sample code. Here's a full featured sample:
public class Program {
public void Main(string[] args) {
IntPtr pSessionInfo;
IntPtr pResumeHandle = IntPtr.Zero;
UInt32 entriesRead, totalEntries;
var netStatus = NativeMethods.NetSessionEnum(
null, // local computer
null, // client name
null, // username
502, // include all info
out pSessionInfo, // pointer to SESSION_INFO_502[]
NativeMethods.MAX_PREFERRED_LENGTH,
out entriesRead,
out totalEntries,
ref pResumeHandle
);
try {
if (netStatus != NativeMethods.NET_API_STATUS.NERR_Success) {
throw new InvalidOperationException(netStatus.ToString());
}
Console.WriteLine("Read {0} of {1} entries", entriesRead, totalEntries);
for (int i = 0; i < entriesRead; i++) {
var pCurrentSessionInfo = new IntPtr(pSessionInfo.ToInt32() + (NativeMethods.SESSION_INFO_502.SIZE_OF * i));
var s = (NativeMethods.SESSION_INFO_502)Marshal.PtrToStructure(pCurrentSessionInfo, typeof(NativeMethods.SESSION_INFO_502));
Console.WriteLine(
"User: {0}, Computer: {1}, Type: {2}, # Open Files: {3}, Connected Time: {4}s, Idle Time: {5}s, Guest: {6}",
s.sesi502_username,
s.sesi502_cname,
s.sesi502_cltype_name,
s.sesi502_num_opens,
s.sesi502_time,
s.sesi502_idle_time,
s.sesi502_user_flags == NativeMethods.SESSION_INFO_502_USER_FLAGS.SESS_GUEST
);
}
} finally {
NativeMethods.NetApiBufferFree(pSessionInfo);
}
}
}
public sealed class NativeMethods {
[DllImport("netapi32.dll", SetLastError=true)]
public static extern NET_API_STATUS NetSessionEnum(
string serverName,
string uncClientName,
string userName,
UInt32 level,
out IntPtr bufPtr,
int prefMaxLen,
out UInt32 entriesRead,
out UInt32 totalEntries,
ref IntPtr resume_handle
);
[DllImport("netapi32.dll")]
public static extern uint NetApiBufferFree(IntPtr Buffer);
public const int MAX_PREFERRED_LENGTH = -1;
public enum NET_API_STATUS : uint {
NERR_Success = 0,
NERR_InvalidComputer = 2351,
NERR_NotPrimary = 2226,
NERR_SpeGroupOp = 2234,
NERR_LastAdmin = 2452,
NERR_BadPassword = 2203,
NERR_PasswordTooShort = 2245,
NERR_UserNotFound = 2221,
ERROR_ACCESS_DENIED = 5,
ERROR_NOT_ENOUGH_MEMORY = 8,
ERROR_INVALID_PARAMETER = 87,
ERROR_INVALID_NAME = 123,
ERROR_INVALID_LEVEL = 124,
ERROR_MORE_DATA = 234 ,
ERROR_SESSION_CREDENTIAL_CONFLICT = 1219
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct SESSION_INFO_502 {
public static readonly int SIZE_OF = Marshal.SizeOf(typeof(SESSION_INFO_502));
public string sesi502_cname;
public string sesi502_username;
public uint sesi502_num_opens;
public uint sesi502_time;
public uint sesi502_idle_time;
public SESSION_INFO_502_USER_FLAGS sesi502_user_flags;
public string sesi502_cltype_name;
public string sesi502_transport;
}
public enum SESSION_INFO_502_USER_FLAGS : uint {
SESS_GUEST = 1,
SESS_NOENCRYPTION = 2
}
}
You don't want to interact with Computer Management, you want to call the same functions it does and cut the middle man out of the loop.
For finding out about current network connection, you might be looking for WNetOpenEnum or NetConnectionEnum
I don't think there's a .NET function for this in the BCL, you'd have to use p/invoke or look for a third-party library (or my personal favorite, use C++/CLI to write a wrapper)

How to group Windows API constants

When defining Windows API const values, is it better to have them as const
public const int SW_HIDE = 0;
public const int SW_SHOWNORMAL = 1;
public const int SW_NORMAL = 1;
public const int SW_SHOWMINIMIZED = 2;
public const int SW_SHOWMAXIMIZED = 3;
public const int SW_MAXIMIZE = 3;
public const int SW_SHOWNOACTIVATE = 4;
public const int SW_SHOW = 5;
public const int SW_MINIMIZE = 6;
public const int SW_SHOWMINNOACTIVE = 7;
public const int SW_SHOWNA = 8;
public const int SW_RESTORE = 9;
public const int SW_SHOWDEFAULT = 10;
public const int SW_MAX = 10;
[DllImport( "user32.dll" )]
public static extern bool ShowWindow( HandleRef hWnd, int nCmdShow );
or to group them together as an enum.
public enum SW {
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
[DllImport( "user32.dll" )]
public static extern bool ShowWindow( HandleRef hWnd, SW nCmdShow );
Group them as enums.
Why? Ints are used all over the place and you can pass them where, for example, a size is necessary as well.. That led to the frickin hungarian notation (szSomething..) in the first place. The type system was lacking and they tried to "fix" it using the variable naming scheme. You're now better off, with a better type system; use it.
Define enums, group them in a sensible way and you won't Thread.Sleep(WM_User) someday (Yes, I'm not completely serious with this example, but I think you get the point).
Except for code maintainability, it doesn't matter at all.
I recommend using an enum; this allows you to use IntelliSense when calling the function, and can help prevent mistakes.
However, you should give your enum a meaningful name, such as WindowShowType.
Also, you might want to remove the prefixes, and perhaps standardize the names to CamelCase.

passing array of structs from c# to regular dll

I have a regular dll with the following function exported.
extern "C" __declspec(dllexport) int FindNearestStuff(double _latitude, double _longitude , LocationStruct * locations[])
LocationStruct is very simple
struct LocationStruct
{
long positionIndex;
long item;
};
I'm tryign to call it from c# using
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude,
ref LocationStruct [] locations);
It's all cool and funky and I can step into the dll function from the debugger.
Inside the dll the LocationStruct array is populated correctly and all is very good.
The problem I have is when it returns back from the dll, the LocationStruct array is not coming back with the data - just empty values...
What am I missing?
thanks so much for your help - you certainly put me onthe right direction and i really appreciate your assistance!
This is the solution which seems to work for me;
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude, IntPtr locations);
public static int FindNearestStuff(double _latitude, double _longitude, LocationStruct[] locations)
{
int returnValue = -1;
LocationStruct temp;
temp.roadIndex = 1;
temp.tdist = 1;
int iStructSize = Marshal.SizeOf(temp);
try
{
IntPtr locationsPtr = IntPtr.Zero;
IntPtr buffer = Marshal.AllocHGlobal(iStructSize * 10);
FindNearestRoads(_latitude, _longitude, buffer);
for (int i = 0; i < 10; i++)
{
IntPtr ptr = new IntPtr(buffer.ToInt32() + iStructSize * i);
locations[i] = (LocationStruct)Marshal.PtrToStructure(ptr, typeof(LocationStruct));
}
returnValue = 0;
}
catch
{
}
return returnValue;
}
I'm not sure that you will be able to do this automatically since C# has no way of knowing how many items are returned in the locations variable (I'm assuming that the return value of FindNearestStuff is the number of entries in locations.)
You will have to manually marshal your data using the Marshall class and a process like this:
[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude,
out IntPtr locations);
public static LocationStruct[] FindNearestStuff(double latitude, double longitude) {
IntPtr locationsPtr = IntPtr.Zero;
int numLocations = FindNearestStuff(latitude, longitude, out locationsPtr);
LocationsStruct[] locations = new LocationsStruct[numLocations];
for (int i = 0; i < numLocations; i++) {
// locationsPtr is a pointer to the struct, so read the value
// at locationPtr which will be the address of the struct and
// then manually marshal the struct from that address
locaitonsStruct[i] = (LocationStruct)Marshal.PtrToStructure(
Marshal.ReadIntPtr(locationsPtr), typeof(LocationsStruct));
// Move to the location pointer to the next address of a
// pointer to a struct
locationsPtr += IntPtr.Size;
}
return locations;
}
I haven't actually tried this so caveat emptor.

Categories