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;
}
Related
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.
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.
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.
I've just recently started playing with the InjectTouchInput for Windows 8 Consumer Preview. I've gone round and round in circles trying to get the darn thing to work but just can't seem to get it to actually interact. I'm working in c# and at the moment am only creating a metro interface with x and y coordinates in two text boxes and buttons which call the functions below to touch on the screen at those coordinates. Is this the right way to go about doing this?
protected unsafe class TouchDriver
{
public struct POINTER_TOUCH_INFO {
public POINTER_INFO pointerInfo; // An embedded POINTER_INFO header structure.
public TOUCH_FLAGS touchFlags; // Currently none.
public Rect rcContact; // Pointer contact area in pixel screen coordinates. By default, if the device does not report a contact area, this field defaults to a 0-by-0 rectangle centered around the pointer location.
public UInt32 orientation; // A pointer orientation, with a value between 0 and 359, where 0 indicates a touch pointer aligned with the x-axis and pointing from left to right; increasing values indicate degrees of rotation in the clockwise direction.
// This field defaults to 0 if the device does not report orientation.
public UInt32 pressure; // Pointer pressure normalized in a range of 0 to 256.
// This field defaults to 128 if the device does not report pressure.
// Question: Can this go from 0 to 1024 to match pen pressure?
}
public enum TOUCH_FLAGS
{
TOUCH_FLAGS_NONE = 0x00000000
}
public POINTER_TOUCH_INFO create_pointer_touch_info(POINTER_INFO pointerInfo, TOUCH_FLAGS touchFlags, RECT rcContact, UInt32 orientation, UInt32 pressure)
{
POINTER_TOUCH_INFO mi = new POINTER_TOUCH_INFO();
mi.pointerInfo = pointerInfo;
mi.touchFlags = touchFlags;
mi.rcContact = rcContact;
mi.orientation = orientation;
mi.pressure = pressure;
return mi;
}
public enum POINTER_INPUT_TYPE
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public struct POINTER_INFO
{
public POINTER_INPUT_TYPE pointerType;
public UInt32 pointerId;
public UInt32 frameId;
public HANDLE sourceDevice;
public HWND hwndTarget;
public Point ptPixelLocation;
public Point ptHimetricLocation;
public Point ptPixelLocationPredicted;
public Point ptHimetricLocationPredicted;
public POINTER_FLAGS pointerFlags;
public DWORD dwTime;
public UInt32 historyCount;
// public UInt32 inputData;
public DWORD dwKeyStates;
public ULONGLONG Reserved;
}
public POINTER_INFO create_pointer_info(
POINTER_INPUT_TYPE pointerType,
UInt32 pointerId,
UInt32 frameId,
HANDLE sourceDevice,
HWND hwndTarget,
Point ptPixelLocation,
Point ptHimetricLocation,
Point ptPixelLocationPredicted,
Point ptHimetricLocationPredicted,
POINTER_FLAGS pointerFlags,
DWORD dwTime,
UInt32 historyCount,
// UInt32 inputData,
DWORD dwKeyStates,
ULONGLONG Reserved)
{
POINTER_INFO mi = new POINTER_INFO();
mi.pointerType = pointerType;
mi.pointerId = pointerId;
mi.frameId = frameId;
mi.sourceDevice = sourceDevice;
mi.hwndTarget = hwndTarget;
mi.ptPixelLocation = ptPixelLocation;
mi.ptHimetricLocation = ptHimetricLocation;
mi.ptPixelLocationPredicted = ptPixelLocationPredicted;
mi.ptHimetricLocationPredicted = ptHimetricLocationPredicted;
mi.pointerFlags = pointerFlags;
mi.dwTime = dwTime;
mi.historyCount = historyCount;
// mi.inputData = inputData;
mi.dwKeyStates = dwKeyStates;
mi.Reserved = Reserved;
return mi;
}
public enum POINTER_FLAGS
{
POINTER_FLAG_NONE = 0x00000000,
POINTER_FLAG_NEW = 0x00000001,
POINTER_FLAG_INRANGE = 0x00000002,
POINTER_FLAG_INCONTACT = 0x00000004,
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
POINTER_FLAG_SECONDBUTTON = 0x00000020,
POINTER_FLAG_THIRDBUTTON = 0x00000040,
POINTER_FLAG_OTHERBUTTON = 0x00000080,
POINTER_FLAG_PRIMARY = 0x00000100,
POINTER_FLAG_CONFIDENCE = 0x00000200,
POINTER_FLAG_CANCELLED = 0x00000400,
POINTER_FLAG_DOWN = 0x00010000,
POINTER_FLAG_UPDATE = 0x00020000,
POINTER_FLAG_UP = 0x00040000,
POINTER_FLAG_WHEEL = 0x00080000,
POINTER_FLAG_HWHEEL = 0x00100000
}
[System.Runtime.InteropServices.DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
private static extern Boolean InjectTouchInput(UInt32 count, POINTER_TOUCH_INFO* pntTchInfo);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern Boolean InitializeTouchInjection(UInt32 maxCount, DWORD dwMode);
private const UInt32 MAX_TOUCH_COUNT = 256; // Can be as high as 256
private const UInt32 TOUCH_FEEDBACK_DEFAULT = 0x1;
private const UInt32 TOUCH_FEEDBACK_INDIRECT = 0x2;
private const UInt32 TOUCH_FEEDBACK_NONE = 0x3;
public unsafe static void MouseTouch(int x, int y)
{
bool ret = false;
ret = InitializeTouchInjection(1, TOUCH_FEEDBACK_DEFAULT);
if (!ret)
{
throw new NotSupportedException();
}
Point point = new Point(x,y);
POINTER_INFO ptrInfo = new POINTER_INFO();
POINTER_TOUCH_INFO* ptrTchInfo;
ptrInfo.pointerType = POINTER_INPUT_TYPE.PT_TOUCH;
ptrInfo.pointerId = 1;
ptrInfo.ptPixelLocation = point;
ptrInfo.pointerFlags = POINTER_FLAGS.POINTER_FLAG_PRIMARY;
POINTER_TOUCH_INFO ptrTchInfobase = new POINTER_TOUCH_INFO();
ptrTchInfo = &ptrTchInfobase;
ptrTchInfo->pointerInfo = ptrInfo;
ptrTchInfo->touchFlags = TOUCH_FLAGS.TOUCH_FLAGS_NONE;
ptrTchInfo->rcContact.X = x - 2;
ptrTchInfo->rcContact.Y = y - 2;
ptrTchInfo->rcContact.Width = 4;
ptrTchInfo->rcContact.Height = 4;
ptrTchInfo->pressure = 128;
ptrTchInfo->orientation = 0;
ret = InjectTouchInput(1, ptrTchInfo);
if (!ret)
{
throw new NotImplementedException();
}
}
}
Almost all of that I've tried to lift from the InjectTouchInput API I found online. I can InitializeTouchInject fine, its the Inject bit thats returning false and I have no idea why.
I went ahead and create some custom functions in c++ based on the sample Microsoft provided and then went and imported that into C# using alot of the same definitions as before but without farting about with all the type checking, pointers and arguments that were causing headaches before.
The DLL file that I've used is TouchInjectionDriver.dll and can be found here:
http://www.mediafire.com/file/do2h6m04omjweb3/TouchInjectionDriver.zip
Below is the C# code I used to implement it.
public enum TOUCH_MASK : uint
{
TOUCH_MASK_NONE = 0x00000000,
TOUCH_MASK_CONTACTAREA = 0x00000001,
TOUCH_MASK_ORIENTATION = 0x00000002,
TOUCH_MASK_PRESSURE = 0x00000004
}
public enum POINTER_INPUT_TYPE : uint
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public enum POINTER_FLAGS : uint
{
POINTER_FLAG_NONE = 0x00000000,
POINTER_FLAG_NEW = 0x00000001,
POINTER_FLAG_INRANGE = 0x00000002,
POINTER_FLAG_INCONTACT = 0x00000004,
POINTER_FLAG_FIRSTBUTTON = 0x00000010,
POINTER_FLAG_SECONDBUTTON = 0x00000020,
POINTER_FLAG_THIRDBUTTON = 0x00000040,
POINTER_FLAG_OTHERBUTTON = 0x00000080,
POINTER_FLAG_PRIMARY = 0x00000100,
POINTER_FLAG_CONFIDENCE = 0x00000200,
POINTER_FLAG_CANCELLED = 0x00000400,
POINTER_FLAG_DOWN = 0x00010000,
POINTER_FLAG_UPDATE = 0x00020000,
POINTER_FLAG_UP = 0x00040000,
POINTER_FLAG_WHEEL = 0x00080000,
POINTER_FLAG_HWHEEL = 0x00100000
}
public enum TOUCH_FEEDBACK : uint
{
TOUCH_FEEDBACK_DEFAULT = 0x1,
TOUCH_FEEDBACK_INDIRECT = 0x2,
TOUCH_FEEDBACK_NONE = 0x3
}
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool InjectTouch(int x, int y, POINTER_INPUT_TYPE pt_input, int pressure, int orientation, int id, int rcContactTop, int rcContactBottom, int rcContactLeft, int rcContactRight, POINTER_FLAGS pointerFlags, TOUCH_MASK touchMask);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setTouchFeedback(TOUCH_FEEDBACK fb);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultRectSize(int size);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultPressure(int pres);
[DllImport("TouchInjectionDriver.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setDefaultOrientation(int or);
[DllImport("User32.dll")]
static extern Boolean MessageBeep(UInt32 beepType);
public static void mouseclick(int x, int y)
{
bool ret;
setTouchFeedback(TOUCH_FEEDBACK.TOUCH_FEEDBACK_INDIRECT);
ret = InjectTouch(x, y, POINTER_INPUT_TYPE.PT_TOUCH, 3200, 0, 0, x - 4, x + 4, y - 4, y + 4,POINTER_FLAGS.POINTER_FLAG_DOWN|POINTER_FLAGS.POINTER_FLAG_INCONTACT|POINTER_FLAGS.POINTER_FLAG_INRANGE,TOUCH_MASK.TOUCH_MASK_CONTACTAREA|TOUCH_MASK.TOUCH_MASK_ORIENTATION|TOUCH_MASK.TOUCH_MASK_PRESSURE);
if (ret)
{
ret = InjectTouch(x, y, POINTER_INPUT_TYPE.PT_TOUCH, 3200, 0, 0, x - 4, x + 4, y - 4, y + 4, POINTER_FLAGS.POINTER_FLAG_UP, TOUCH_MASK.TOUCH_MASK_CONTACTAREA | TOUCH_MASK.TOUCH_MASK_ORIENTATION | TOUCH_MASK.TOUCH_MASK_PRESSURE);
}
else
{
MessageBeep(0);
}
}
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