pinvoke - pass a string[] as out parameter - c#

I couldn't get the below p/invoke code working, please help, thanks.
vertices below stays as null after the c++ call. I've tried to use IntPtr instead of string[], IntPtr stays as 0 after c++ call.
c++ code
extern "C" __declspec(dllexport)
float compute_similarity(char** vertices)
{
vertices = new char*[2];
vertices[0] = new char[3];
vertices[1] = new char[3];
strcpy(vertices[0], "he");
strcpy(vertices[1], "ha");
return 1.01;
}
c# code
[DllImport("demo.dll", EntryPoint = "compute_similarity",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern float compute_similarity(out string[] vertices);
//also tried 'static extern float compute_similarity(out IntPtr vertices);'
public string Func()
{
string[] vertices; //also tried 'IntPtr vertices'
float sim = compute_similarity(out vertices);
//break point here vertices stays null(or 0 for IntPtr)
return sim.ToString();
}

I don't think it can be done properly this way, because you have no way of specifying that P/Invoke must free the memory with delete[] calls after converting it to managed strings.
However, with a SAFEARRAY of BSTRs and the MarshalAs attribute, you may have a fighting chance.
extern "C" __declspec(dllexport)
float compute_similarity(SAFEARRAY** vertices)
{
SAFEARRAY *pArr = SafeArrayCreateVector(VT_BSTR, 0, 2);
if(pArr != NULL)
{
LONG index = 0;
BSTR bs = SysAllocString(L"he");
SafeArrayPutElement(pArr, &index, bs);
SysFreeString(bs);
index = 1;
bs = SysAllocString(L"ha");
SafeArrayPutElement(pArr, &index, bs);
SysFreeString(bs);
}
*vertices = pArr;
return 1.01;
}
[DllImport("demo.dll", EntryPoint = "compute_similarity", CallingConvention = CallingConvention.Cdecl)]
static extern float compute_similarity(
[Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] out string[] vertices
);
Of you may still use IntPtr (and do the marshaling manually on the C# side, while exporting a delete_strings function), but remember that your function must take its char** by reference, or it can't actually modify it.

Related

Using WriteConsoleOutput on '/u263a' always prints incorrectly [duplicate]

This question already has an answer here:
Using WriteConsoleOutput to write Unicode with c#
(1 answer)
Closed 2 years ago.
Having looked at this question and modifying the code to print characters like '☺', '☻' or '█', I can't see how to get these characters to actually draw properly with WriteConsoleOutput. (There is no problem when using a stream or Console.Write but I can't individually control the character colour with these solutions so they're unsuitable.)
It seems there was a solution here for C++ but the "L" macro they talk about down the bottom does not apply to C#.
Finally, having checked the suggestions here, I can't see anything wrong with the code I ended up with:
class Program
{
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion
);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCP(uint wCodePageID);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public char UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
static void Main(string[] args)
{
//SetConsoleOutputCP(65001);
//SetConsoleCP(65001);
SafeFileHandle fileHandle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
Console.TreatControlCAsInput = true;
//Console.OutputEncoding = Encoding.UTF8;
Console.OutputEncoding = System.Text.Encoding.Unicode;
Console.CursorVisible = false;
short screenWidth = 40;
short screenHeight = 20;
Console.SetWindowSize(screenWidth + 1, screenHeight + 1);
Console.SetBufferSize(screenWidth + 1, screenHeight + 1);
bool running = true;
while (running)
{
CharInfo[] buf = new CharInfo[screenWidth * screenHeight];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = screenWidth, Bottom = screenHeight };
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = 6;
buf[i].Char.UnicodeChar = '☺';
}
bool b = WriteConsoleOutputW(
fileHandle,
buf,
new Coord() { X = screenWidth, Y = screenHeight },
new Coord() { X = 0, Y = 0 },
ref rect
);
Console.SetCursorPosition(0, 0);
}
}
}
I seem to have ruled out all combinations of CharSet, A/W DLLImport suffixes, code pages, Console properties & source-file encoding.
(Surely it can't be impossible to fast-print coloured smiley-face characters to a C# console. Somebody tell me I've missed something silly here; I've spent about 2 hours Googling & trying to emulate something Castle Adventure managed in 1984 with ease.)
How can I get the aforementioned '☺' to print anything other than a '?' or a ':'?
This seems to be a duplicate of Using WriteConsoleOutput to write Unicode with c#
The solution to that was to add the CharSet = CharSet.Unicode attribute to both the CharInfo and CharUnion structs.

Marshal.Copy not copying over value

I'm not very familiar with C# and I'm trying to use 'Marshal.Copy' but it's not changing the value of the IntPtr that I'm using.
IntPtr ptr = InitPointer(width, height);
Marshal.Copy(inputIntArray, 0, ptr, width * height * 4);
Where InitPointer is defined as:
[DllImport(#"../../../../Debug/KernelApplier.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr InitPointer(int x, int y);
And in my kerneApplier.dll the function is written as:
int * inputBuffer;
int size;
int m_x, m_y;
extern "C" __declspec(dllexport) int* InitPointer(int x, int y) {
size = x*y * sizeof(cl_int3);
m_x = x;
m_y = y;
inputBuffer = (int*)malloc(size * sizeof(int));
return inputBuffer;
}
I'm using my watch window to monitor the values where:
ptr.m_value = 0x0641c040
inputIntArray[0] = 152
0x0641c040 = 104972352 //This does not change after the Marshal.Copy
Am I using Marshal.copy incorrectly or is there a problem passing the data from C++ to C#
Your code is fine. The IntPtr value does not change, but it's the address of the unmanaged memory so it is not expected to change.

C# -> C DLL callback crash after first (successful) iteration

My C# calls a C++DLL in order to obtain data from a device. Data are returned through a callback function which has a window handle as input and one integer and two structs as by ref input values.
When the callback is triggered, the DLL is supposed to return 5 records, i.e. to be triggered 5 times. The first record arrives with all proper values but then the app crashes (0xC0000005: Access violation reading location 0x00000000), so my guess is that it crashes right before sending the second set of data.
I don’t have access to the source code of the DLL but I have access to the C++ app that demonstrates its usage.
This is how I declare the delegate and import the function that contains the callback as a member:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void onGetParaminfoHandler( IntPtr handle, ref int hIndex, ref PATINFO pi, ref PARAMINFO pri);
private static onGetParaminfoHandler ogp; //--declaration reference of the callback to be used
[DllImport("DataTraffic.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
private static extern void StartUpLoad(IntPtr handle, onGetParaminfoHandler myCallback);
Here’s the declaration of the two structs, PATINFO and PARAMINFO:
[StructLayout(LayoutKind.Sequential)]
public struct PATINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szDeviceInfo;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szUserName;//name
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string szTime;
[MarshalAs(UnmanagedType.U4)]
uint nID;
[MarshalAs(UnmanagedType.U4)]
uint nGender;//Sex 1 = male 0: Female
[MarshalAs(UnmanagedType.U4)]
uint nStandard;//Standard 1:ers 2:knudson 3:usa 4: Other
[MarshalAs(UnmanagedType.U4)]
uint nAge;//Age
[MarshalAs(UnmanagedType.U4)]
uint nHeight;//Height cm
[MarshalAs(UnmanagedType.U4)]
uint nWeight;//weight kg
[MarshalAs(UnmanagedType.Bool)]
bool BBDT;//whether relaxing True=Diastolic
[MarshalAs(UnmanagedType.Bool)]
bool bSmoker;//Smoking true= Smoking
};
[StructLayout(LayoutKind.Sequential)]
public struct PARAMINFO
{
//[MarshalAs(UnmanagedType.R4)]
float fffvc;//Maximum force capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEV1;//a second amount
//[MarshalAs(UnmanagedType.R4)]
float fPEF;//Peak flow rate
//[MarshalAs(UnmanagedType.R4)]
float fFEV1Per;//FEV1/FVC
//[MarshalAs(UnmanagedType.R4)]
float fFEF25;//25% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF750;//50% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF75;//75% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFEF2575;//25-75% capacity
//[MarshalAs(UnmanagedType.R4)]
float fFET;//exhalation Time
//[MarshalAs(UnmanagedType.R4)]
float fEVOL;//Extrapolation Volume
//[MarshalAs(UnmanagedType.R4)]
float fEOTV;//End of test volume
//[MarshalAs(UnmanagedType.R4)]
float fPEFT;//Peak Time
};
Here’s how I call the callback function:
public static void StartDataupload(IntPtr handle)
{
ogp = new onGetParaminfoHandler(GetParaminfo); //--keep reference of the delegate
GC.KeepAlive(ogp); //-- not much help from this one
StartUpLoad(handle, ogp);
}
And the callback itself:
private static void GetParaminfo(IntPtr handle, ref int hIndex,ref PATINFO pi, ref PARAMINFO pri)
{ enter code here
if (hIndex >= 1)
{
PATINFO pat = new PATINFO();
pat = pi;
PARAMINFO par = new PARAMINFO();
par = pri;
int error = Marshal.GetLastWin32Error();
if (error != 0)
throw new Win32Exception(error);
}
Some more info: the LastWin32Error returns large, random numbers which of course translate to Unknown errors. I suspect that between the first and the second call of the callback by the DLL, some pointer gets lost and so do I.
Here’s how the sample code (which works fine) in C calls the callback:
StartUpLoad(GetSafeHwnd(), GetParamInfo);
And part of the callback function of the sample C program which also uses the same DLL:
static void GetParamInfo(HWND hWnd, int &nIndex, PATINFO &PatientInf, PARAMINFO &ParamInf)
{
if(nIndex >= 1)
{
PATINFO *pPatientInf = new PATINFO;
PARAMINFO *pParamInf = new PARAMINFO;
*pPatientInf = PatientInf;
*pParamInf = ParamInf;
PostMessage(hWnd, WM_RECEIVEONECASE,
reinterpret_cast<WPARAM>(pPatientInf),
reinterpret_cast<LPARAM>(pParamInf));
}
else if(nIndex == FLAG_UPLOAD_STOP)
{
EnableOk(hWnd);
PostMessage(hWnd, WM_RECEIVECOMPLETE, PatientInf.nID, 0);
}
Any hints as to where to focus, would be greatly appreciated.
edit: here is the dllexport function signature:
__declspec(dllexport) void StartUpLoad(HWND hwnd, void (*pVoid)(HWND hWnd, int &nIndex, PATINFO &PatientInf, PARAMINFO &ParamInf));
and here are the definitions in C for the two structures:
typedef struct PATINFO
{
char szDeviceInfo[20];
char szUserName[20];
char szTime[20];
UINT nID;
UINT nGender;
UINT nStandard;
UINT nAge;
UINT nHeight;
UINT nWeight;
BOOL bBDT;
BOOL bSmoker;
PATINFO()
{
memset(szDeviceInfo, 0, sizeof(szDeviceInfo));
memset(szUserName, 0, sizeof(szUserName));
memset(szTime, 0, sizeof(szTime));
nID = 1;
nGender = 1;
nStandard = 2;
nAge = 20;
nHeight = 160;
nWeight = 50;
bBDT = FALSE;
bSmoker = FALSE;
};
}PATINFO;
typedef struct PARAMINFO
{
float fFVC;
float fFEV1;
float fPEF;
float fFEV1Per;//FEV1/FVC
float fFEF25;
float fFEF50;
float fFEF75;
float fFEF2575;
float fFET;
float fEVOL;
float fEOTV;
float fPEFT;
PARAMINFO()
{
fFVC = 0.0;
fFEV1 = 0.0;
fPEF = 0.0;
fFEV1Per = 0.0;
fFEF25 = 0.0;
fFEF50 = 0.0;
fFEF75 = 0.0;
fFEF2575 = 0.0;
fFET = 0.0f;
fEVOL = 0.0f;
fEOTV = 0.0f;
fPEFT = 0.0f;
};
}PARAMINFO;
EDIT2: the C handler OnReceiveOneCase is the following:
void CWaitDlg::OnReceiveOneCase(WPARAM wParam, LPARAM lParam)
{
PATINFO *pPatientInfo = reinterpret_cast<PATINFO *>(wParam);
PARAMINFO *pParamInfo = reinterpret_cast<PARAMINFO *>(lParam);
int nListCount = 0;
CString strNo("\0");
CString strFVC("\0");
strNo.Format("%d", pPatientInfo->nID);
strFVC.Format("%.2f", pParamInfo->fFVC);
nListCount = m_UploadList.GetItemCount();
m_UploadList.InsertItem(nListCount, strNo);
m_UploadList.SetItemText(nListCount, 1, pPatientInfo->szTime);
m_UploadList.SetItemText(nListCount, 2, strFVC);
++m_nCaseCount;
delete pPatientInfo;
delete pParamInfo;
}

Import c++ DLL to c#

Please help me with DLLImpot in c#.
I have DLL, analysed it with DLL Export Viewer, this class method is shown:
public: static float * __cdecl Evaluator::calculateLM(float *,float *,int,int,float *,float *)
I just can't figure out how to DllImport it into c#.
Finally figured it out.
[DllImport("LMModelSolve.dll",
EntryPoint = "?calculateLM#Evaluator##SAPAMPAM0HH00#Z",
CallingConvention = CallingConvention.Cdecl)
]
static extern IntPtr calculateLM(float[] x, float[] y, int n, int iterations, float[] lower, float[] upper);
And to call and get result:
IntPtr res = calculateLM(x, y, ndata, 200, lower, upper);
float[] resultVertices = new float[4];
Marshal.Copy(res,resultVertices,0,4);

How to reserve a row for input in multi-threaded Console?

This question has bugged me for a while now, and I realize it's hard to describe what I am looking for. I want to be able to reserve a row for text input in a C# Console Application, while still allowing other information to be updated in the remaining rows. More specifically, I'd like to make a small mud game where the game is updated even while the user is busy making input. It's important that the input doesn't block the information flow.
I'd like to achieve the effect of the user writing input to the last visible row in the screen, while the other text append as usual, but not scrolling down my line of input, nor overwrite it.
If I would describe this in terms of Forms, I'd imagine the equivalent of having a multi-line textbox as the upper portion for the information, with a single-line textbox at the bottom for the input.
One option that you could try, is to directly manipulate the console buffer to render your game area and use the Console.SetCursorPosition to position the cursor to the input line where you use Console.ReadLine for example to take the user input.
Since the direct manipulation of the buffer does not affect the cursor position and is independent of the Console Read/Write functionality you can have a thread updating the Console buffer which covers the first 24 lines and the 25 line is waiting for input. If I get some time I will try put together a sample of what I mean, but in the meantime you can reference the other answers I have provided for a pointer to writing directly to the Console buffer.
How can I write fast colored output to Console?
Deleting previously written lines in Console
Of course you will want to write some nice wrapper functions to make this easy to work with, I always think about doing this, I just don't do enough work with the console so that I actually get down and do something.
Update: Added a small example of updating the console in a thread while still accepting user input. Just type 'quit' to stop it running. Note the the ConsoleBuffer class is not ideal, I am not closing the console handle, it was just a quick piece of code for the demo.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Threading;
namespace ConsoleDemo
{
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(UpdateConsole));
t.IsBackground=true;
t.Start();
string input;
do
{
Console.SetCursorPosition(0, 23);
Console.Write("Command: ");
input = Console.ReadLine();
ConsoleBuffer.ClearArea(0, 21, 80, 3);
Console.SetCursorPosition(0, 22);
Console.Write(input);
} while (!string.Equals(input, "quit", StringComparison.OrdinalIgnoreCase));
}
static void UpdateConsole()
{
int i = 0;
Random rnd = new Random();
while (true)
{
string s = new string((char)(65 + (i % 26)),1);
for (short x = 0; x < 80; ++x)
{
for (short y = 0; y < 20; ++y)
{
ConsoleBuffer.WriteAt(x, y, s);
ConsoleBuffer.SetAttribute(x, y, (short)(rnd.Next(15)+1));
}
}
Thread.Sleep(500);
i++;
}
}
}
public class ConsoleBuffer
{
private static SafeFileHandle _hBuffer = null;
static ConsoleBuffer()
{
_hBuffer = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (_hBuffer.IsInvalid)
{
throw new Exception("Failed to open console buffer");
}
}
public static void WriteAt(short x, short y, string value)
{
int n = 0;
WriteConsoleOutputCharacter(_hBuffer, value, value.Length, new Coord(x, y), ref n);
}
public static void SetAttribute(short x, short y, short attr)
{
SetAttribute( x, y, new short[] { attr });
}
public static void SetAttribute(short x, short y, short[] attrs)
{
int n = 0;
WriteConsoleOutputAttribute(_hBuffer, attrs, attrs.Length, new Coord(x, y), ref n);
}
public static void ClearArea(short left, short top, short width, short height, char ch = ' ')
{
ClearArea(left, top, width, height, new CharInfo() { Char = new CharUnion() { UnicodeChar = ch } });
}
public static void ClearArea(short left, short top, short width, short height)
{
ClearArea(left, top, width, height, new CharInfo() { Char = new CharUnion() { AsciiChar = 32 } });
}
private static void ClearArea(short left, short top, short width, short height, CharInfo charAttr)
{
CharInfo[] buf = new CharInfo[width * height];
for (int i = 0; i < buf.Length; ++i)
{
buf[i] = charAttr;
}
SmallRect rect = new SmallRect() { Left = left, Top = top, Right = (short)(left + width), Bottom = (short)(top + height) };
WriteConsoleOutput(_hBuffer, buf,
new Coord() { X = width, Y = height },
new Coord() { X = 0, Y = 0 },
ref rect);
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputCharacter(
SafeFileHandle hConsoleOutput,
string lpCharacter,
int nLength,
Coord dwWriteCoord,
ref int lpumberOfCharsWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputAttribute(
SafeFileHandle hConsoleOutput,
short[] lpAttributes,
int nLength,
Coord dwWriteCoord,
ref int lpumberOfAttrsWritten);
[StructLayout(LayoutKind.Sequential)]
struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
struct CharUnion
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
struct CharInfo
{
[FieldOffset(0)]
public CharUnion Char;
[FieldOffset(2)]
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
}
The dotNet Console supports SetCursorPosition() and you also use the old DOS trick of ending a line with \r instead of \n\r.
But multi-threading and Append doesn't sound like a good combination.
Look at these .NET bindings for curses
http://www.mono-project.com/Libraries#Curses
ncurses is obviously a UNIX invention, but the API's are said to be mostly cross-platform (I haven't tried the .NET bindings myself, but have had very good results working with ncurses in general).
This will absolutely contain the goods you need and more

Categories