Marshal.Copy not copying over value - c#

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.

Related

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;
}

BitBlt convert to byte array and parse from c++ to c#

I'm trying to create a Screen Capture DLL in C++ and send the resulting Byte Arrays to C#.
I'm able to get the size returned to C# but the byte array is always null.
Here's the C++ code (made up of bits i found on the internet)
__declspec(dllexport) int ScreenCap(BYTE* *data, DWORD *size)
{
try
{
//BITMAP bmpScreen;
HWND DesktopHwnd = GetDesktopWindow();
RECT DesktopParams;
HDC DevC = GetDC(DesktopHwnd);
GetWindowRect(DesktopHwnd,&DesktopParams);
DWORD Width = DesktopParams.right - DesktopParams.left;
DWORD Height = DesktopParams.bottom - DesktopParams.top;
DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*4));
*size = FileSize;
char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);
PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
BFileHeader->bfType = 0x4D42; // BM
BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
BInfoHeader->biPlanes = 1;
BInfoHeader->biBitCount = 24;
BInfoHeader->biCompression = BI_RGB;
BInfoHeader->biHeight = Height;
BInfoHeader->biWidth = Width;
RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
RGBTRIPLE color;
HDC CaptureDC = CreateCompatibleDC(DevC);
HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
SelectObject(CaptureDC,CaptureBitmap);
BOOL bRet = BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT);
//GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
//GetObject(CaptureBitmap,sizeof(BITMAPFILEHEADER),&bmpScreen);
//BYTE* lpPixels = new BYTE[sizeof((LPBITMAPINFO)BInfoHeader)];
GetDIBits(CaptureDC, CaptureBitmap, 0, Height, *data, (LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
//DWORD Junk;
//DIBSECTION dib;
//GetObject(CaptureBitmap, sizeof(dib), (LPVOID)&dib);
//HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
//WriteFile(FH,BmpFileData,FileSize,&Junk,0);
//CloseHandle(FH);
GlobalFree(BmpFileData);
return 1;
}
catch(char *p)
{
return 0;
}
}
And here's the C# code i'm using with the "ref" keyword.
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int ScreenCap(ref byte[] data, ref int size);
public static void CapScreen()
{
try
{
int hr = 0;
byte[] gData = null;
int gSize = 0;
hr = ScreenCap(ref gData, ref gSize);
int a = 1;
}
catch(Exception ex)
{
int a = 1;
}
I'm thinking there may be an issue with my GetDIBits but i'm not sure. I hope some of you guru's can set me on the right path. Thanks.
* EDIT *
OK Big thanks to both jdweng and filip for pointing me in the right direction. i'm now receiving the screen as an IntPtr and able to render it to my C# app. here's the code that now works (kind of i'll explain at the bottom)
__declspec(dllexport) char* ScreenCap(DWORD * size)
{
try
{
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
*size = ((((24 * nScreenWidth + 31)&(~31)) / 8)*nScreenHeight);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hdcScreen = GetDC(GetDesktopWindow());
HDC hdcCompatible = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight);
//HGDIOBJ hOldBmp = SelectObject(hdcCompatible, hBmp);
HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp);
BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done
// Get the BITMAPINFO structure from the bitmap
GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS);
// create the bitmap buffer
char* lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biBitCount = 24;
// get the actual bitmap buffer
GetDIBits(hdcScreen, hBmp, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS);
//Clean Up
DeleteDC(hdcCompatible);
ReleaseDC(GetDesktopWindow(), hdcScreen);
DeleteDC(hdcScreen);
DeleteObject(hBmp);
//DeleteObject(hOldBmp);
return lpPixels;
}
catch(char *p)
{
return 0;
}
}
and the C#
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ScreenCap(ref int size);
while(true)
{
int size = 0;
IntPtr ptr = ScreenCap(ref size);
byte[] result = new byte[size];
Marshal.Copy(ptr, result, 0, size);
Marshal.Release(ptr);
ptr = IntPtr.Zero;
MainWindow.Dispatcher.Invoke(new Action(
() =>
{
System.Windows.Media.ImageSource ThumbnailImage = System.Windows.Media.Imaging.BitmapSource.Create(1920, 1080, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, result, 1920 * 3);
MainWindow.canvasMain.Background = new System.Windows.Media.ImageBrush(ThumbnailImage);
result = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
), null);
}
Not sure if the problem i now face is due to doing it this way but i'm getting a major memory leak from the C++ dll now. If this is another issue all together, do i need to create a new thread?
** Edit **
Problem solved by creating a new function in the C++ dll to delete the returned char array. may not be elegant but it works.
i'd like to give a big thanks to Filip who's comments helped me to look in all sorts of places and see other things. Jdweng, your IntPtr was a god send. Not sure how i set this as answered by Jdweng. would love to award to you both.
Here is what worked for me if anyone is looking for the same. I'd like to send a big thanks to both Filip Kocica and jdweng who's suggestions gave me some ideas and helped very much. Thanks and appreciated very much.
C++
char* lpPixels;
__declspec(dllexport) BOOL ScreenCapClean()
{
delete[] lpPixels;
return 1;
}
__declspec(dllexport) char* ScreenCap(DWORD * size)
{
try
{
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
*size = ((((24 * nScreenWidth + 31)&(~31)) / 8)*nScreenHeight);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
MyBMInfo.bmiHeader.biWidth = nScreenWidth;
MyBMInfo.bmiHeader.biHeight = -nScreenHeight;
MyBMInfo.bmiHeader.biPlanes = 1;
MyBMInfo.bmiHeader.biBitCount = 24;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biSizeImage = 0;
MyBMInfo.bmiHeader.biXPelsPerMeter = 0;
MyBMInfo.bmiHeader.biYPelsPerMeter = 0;
MyBMInfo.bmiHeader.biClrUsed = 0;
MyBMInfo.bmiHeader.biClrImportant = 0;
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hdcScreen = GetDC(0);
HDC hdcCompatible = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight);
HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp);
BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done
// Get the BITMAPINFO structure from the bitmap
GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS);
// create the bitmap buffer
lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biBitCount = 24;
// get the actual bitmap buffer
GetDIBits(hdcScreen, hBmp, 0, -MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS);
//Clean Up
ReleaseDC(0, hdcScreen);
ReleaseDC(0, hdcCompatible);
DeleteDC(hdcCompatible);
DeleteDC(hdcScreen);
DeleteObject(hBmp);
DeleteObject(hOldBmp);
return lpPixels;
}
catch(char *p)
{
return 0;
}
}
C#
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ScreenCap(ref int size);
[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool ScreenCapClean();
int size = 0;
IntPtr ptr = ScreenCap(ref size);
byte[] result = new byte[size];
Marshal.Copy(ptr, result, 0, size);
Marshal.Release(ptr);
ptr = IntPtr.Zero;
//After doing what's required with the byte array
bool hr = ScreenCapClean();

System.AccessViolationException with double pointer?

I want to compress textures to pvrtc in managed code so I've found a source which does what I want but I get a System.AccessViolationException. This is the code I use so far:
[DllImport("x86/pvrtc.dll", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CompressTexture(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression, ref IntPtr dataSizes);
public static byte[] Compress(byte[] data, int height, int width, int mipLevels, bool preMultiplied, bool pvrtc4bppCompression)
{
IntPtr dataSizesPtr = Marshal.AllocCoTaskMem(data.Length + 1);
var texDataPtr = CompressTexture(data, height, width, mipLevels, preMultiplied, pvrtc4bppCompression, ref dataSizesPtr);
//Store the size of each mipLevel
var dataSizesArray = new int[mipLevels];
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
Marshal.Copy(dataSizesPtr, dataSizesArray, 0, dataSizesArray.Length);
var levelSize = 0;
byte[] levelData = new byte[0];
for (int x = 0; x < mipLevels; x++)
{
levelSize = dataSizesArray[x];
levelData = new byte[levelSize];
Marshal.Copy(texDataPtr, levelData, 0, levelSize);
texDataPtr = IntPtr.Add(texDataPtr, levelSize);
}
return levelData;
}
Any help is very much appreciated.

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