Using Marshal.Copy correctly - c#

I have a working wrapper class for Dallmeier camera devices, It contains a callback method to receive the current YUV image.
See details C# wrapper for array of three pointers.
I have a button on my form that gets the YUV Image. The callback returns 'yuvData' which is an array of three pointers to Y, U, and V part of image.
I then copy the three pointers into thier own pointer and then copy them into a byte array. The yuvCallback continues to run until I disconnect the camera.
Am I using Marshal.Copy correctly?
public class DLMSDK
{
public delegate int YUVDataCallback(dlm_yuvdataParametersStructure pParameters);
DllImport(#"DallmeiersDLL\davidapileolive.dll")]
public extern static int dlm_setYUVDataCallback(int SessionHandle, YUVDataCallback dataCallback);
[StructLayout(LayoutKind.Explicit, Size = 32)]
public struct dlm_yuvdataParametersStructure
{
[FieldOffset(0)]
public int IPlayerID;
[FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
public IntPtr[] yuvData;
[FieldOffset(8), MarshalAs(UnmanagedType.ByValArray, SizeConst=1)]
public IntPtr[] pitch;
[FieldOffset(12)]
public int width;
[FieldOffset(16)]
public int height;
[FieldOffset(18)]
public long ts;
[FieldOffset(28), MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public IntPtr[] extData;
}
}
public partical class Form1 : Form
{
int width; int height;
int yArraySize; int uvArraySize;
byte[] yBytes; byte[] uBytes; byte[] vBytes;
int sizeY; int sizeU; int sizeV;
IntPtr ptrY; IntPtr ptrU; IntPtr ptrV;
DLMSDK.YuvDataCallback yuvdataCallback;
private void Form1_Load(object send, EventArgs e)
{
error = DLMSDK.dlm_initSDK();
if (error == 0)
registerEvents();
}
private void registerEVents()
{
yuvdataCallback = yuvdataHandler;
}
private void btnGetYUV_Click(object sender, EventArgs e)
{
try
{
error = DLMSDK.dlm_setYUVDataCallback(SessionHandle, yuvdataCallback);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public int yuvdataHandler(DLMSDK.dlm_yuvdataParametersStructure pParameters)
{
width = pParameters.width;
height = pParameters.height;
yArraySize = width * height;
uvArraySize = yArraySize/4;
yBytes = new byte[yArraySize];
uBytes = new byte[uvArraySize];
vBytes = new byte[uvArraySize];
sizeY = Marshal.SizeOf(yBytes[0]) * yBytes.Length;
ptrY = Marshal.AllocHGlobal(sizeY);
sizeU = Marshal.SizeOf(uBytes[0]) * uBytes.Length;
ptrU = Marshal.AllocHGlobal(sizeU);
sizeV = Marshal.SizeOf(vBytes[0]) * vBytes.Length;
ptrV = Marshal.AllocHGlobal(sizeV);
try
{
// Copy the three pointers to Y,U, & V pointers
Marshal.Copy(pParameters.yuvData, 0, ptrY, 1);
Marshal.Copy(pParameters.yuvData, 1, ptrU, 1);
Marshal.Copy(pParameters.yuvData, 2, ptrV, 1);
// Copy pointers to YUV byte arrays
Marshal.Copy(ptrY, yBytes, 0, sizeY);
Marshal.Copy(ptrU, uBytes, 0, sizeU);
Marshal.Copy(ptrV, vBytes, 0, sizeV);
// Convert Y (Luminance) to Greyscale and display
Bitmap bmp = ImgConvert.ToGreyscale(yBytes, width, height);
DisplayImage(bmp);
}
finally
{
if (ptrY != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrY);
ptrY = IntPtr.Zero;
}
if (ptrU != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrU);
ptrU = IntPtr.Zero;
}
if (ptrV != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrV);
ptrV = IntPtr.Zero;
}
}
return 0;
}
}

I see you posted a similar question over at the MSDN forums: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/67d20c16-d58b-444d-9689-88fab2792ab1
As I wrote over there, it's not correct to pack the callback delegate's parameters into a structure and pass it by value. So the first step is to correct the delegate signature.

Related

C# Camera Capture with AviCap32.dll

I'm trying to capture images from my webcam by using avicap32.dll. An event should be called when a new image is available. The problem is that it only shows a black image with a few rows of colored pixels at the bottom (Sample image).
I DON'T want to use any other libraries like Aforge, DirectShow, OpenCV or something similar.
Here is my code:
public class CameraCapture2
{
[DllImport("user32", EntryPoint = "SendMessage")]
static extern bool SendMessage(int hWnd, uint wMsg, int wParam, int lParam);
[DllImport("user32", EntryPoint = "SendMessage")]
static extern int SendBitmapMessage(int hWnd, uint wMsg, int wParam, ref BITMAPINFO lParam);
[DllImport("user32", EntryPoint = "SendMessage")]
static extern int SendHeaderMessage(int hWnd, uint wMsg, int wParam, CallBackDelegate lParam);
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);
delegate void CallBackDelegate(IntPtr hwnd, ref VIDEOHEADER hdr);
CallBackDelegate delegateFrameCallBack;
AutoResetEvent autoEvent = new AutoResetEvent(false);
Thread frameThread;
public event EventHandler<NewFrameEventArgs> NewFrame;
public int FPS = 0;
public const int BitsPerPixel = 24; //RGB (by changing this value, change the other PixelFormats)
public int frameWidth = 0;
public int frameHeight = 0;
public int camID = 0;
int camHwnd, parentHwnd;
bool bStart = false;
object threadLock = new object();
int preferredFPSms;
public CameraCapture2(int frameWidth, int frameHeight, int preferredFPS, int camID, int parentHwnd)
{
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
this.parentHwnd = parentHwnd;
this.camID = camID;
PreferredFPS = preferredFPS;
delegateFrameCallBack = FrameCallBack;
}
public void Start()
{
try
{
camHwnd = capCreateCaptureWindow("WebCam", 0, 0, 0, frameWidth, frameHeight, parentHwnd, camID);
// connect to the device
if (SendMessage(camHwnd, WM_CAP.WM_CAP_DRIVER_CONNECT, 0, 0))
{
BITMAPINFO bInfo = new BITMAPINFO();
bInfo.bmiHeader = new BITMAPINFOHEADER();
bInfo.bmiHeader.biSize = (uint)Marshal.SizeOf(bInfo.bmiHeader);
bInfo.bmiHeader.biWidth = frameWidth;
bInfo.bmiHeader.biHeight = frameHeight;
bInfo.bmiHeader.biPlanes = 1;
bInfo.bmiHeader.biBitCount = BitsPerPixel; // bits per pixel, 24 - RGB
//Enable preview mode. In preview mode, frames are transferred from the
//capture hardware to system memory and then displayed in the capture
//window using GDI functions.
SendMessage(camHwnd, WM_CAP.WM_CAP_SET_PREVIEW, 1, 0);
SendMessage(camHwnd, WM_CAP.WM_CAP_SET_PREVIEWRATE, 34, 0); // sets the frame display rate in preview mode
SendBitmapMessage(camHwnd, WM_CAP.WM_CAP_SET_VIDEOFORMAT, Marshal.SizeOf(bInfo), ref bInfo);
frameThread = new Thread(new ThreadStart(this.FrameGrabber));
bStart = true; // First, set variable
frameThread.Priority = ThreadPriority.Lowest;
frameThread.Start(); // Only then put thread to the queue
}
else
throw new Exception("Cannot connect to device");
}
catch (Exception e)
{
Stop();
throw new Exception("Error: " + e.Message);
}
}
public void Stop()
{
try
{
bStart = false;
Set();
SendMessage(camHwnd, WM_CAP.WM_CAP_DRIVER_DISCONNECT, 0, 0);
}
catch { }
}
private void FrameGrabber()
{
while (bStart) // if worker active thread is still required
{
try
{
// get the next frame. This is the SLOWEST part of the program
SendMessage(camHwnd, WM_CAP.WM_CAP_GRAB_FRAME_NOSTOP, 0, 0);
SendHeaderMessage(camHwnd, WM_CAP.WM_CAP_SET_CALLBACK_FRAME, 0, delegateFrameCallBack);
}
catch (Exception excep)
{
this.Stop(); // stop the process
throw new Exception("Capturing error:\r\n" + excep.Message);
}
}
}
/// <summary>
/// Allow waiting worker (FrameGrabber) thread to proceed
/// </summary>
public void Set()
{
autoEvent.Set();
}
private void FrameCallBack(IntPtr hwnd, ref VIDEOHEADER hdr)
{
if (NewFrame != null)
{
//Bitmap bmp = new Bitmap(frameWidth, frameHeight, frameWidth * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, hdr.lpData);
byte[] _imageTemp = new byte[hdr.dwBufferLength];
Marshal.Copy(hdr.lpData, _imageTemp, 0, (int)hdr.dwBufferLength);
Bitmap bmp = new Bitmap(new System.IO.MemoryStream(WriteBitmapFile(frameWidth, frameHeight, _imageTemp)));
NewFrame(this, new NewFrameEventArgs(bmp));
}
// block thread for preferred milleseconds
if (preferredFPSms == 0)
autoEvent.WaitOne();
else
autoEvent.WaitOne(preferredFPSms, false);
}
private static byte[] WriteBitmapFile(int width, int height, byte[] imageData)
{
using (var stream = new System.IO.MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
{
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height)
, System.Drawing.Imaging.ImageLockMode.WriteOnly
, bmp.PixelFormat
);
Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);
bmp.UnlockBits(bmpData);
if (bmp == null)
return null;
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // get bitmap bytes
return ms.ToArray();
} // End Using stream
}
}
public void ShowVideoDialog()
{
SendMessage(camHwnd, WM_CAP.WM_CAP_DLG_VIDEODISPLAY, 0, 0);
}
public int PreferredFPS
{
get { return 1000 / preferredFPSms; }
set
{
if (value == 0)
preferredFPSms = 0;
else if (value > 0 && value <= 30)
{
preferredFPSms = 1000 / value;
}
}
}
}
public class NewFrameEventArgs
{
public Bitmap Frame;
public NewFrameEventArgs(Bitmap frame)
{
Frame = frame;
}
}
Most of this code is from Codeproject

Drawing lines behind textbox C#

I am working on creating a simple notebook application. I have been asked to make the input area look like a sheet of notebook paper, with the text sitting on light blue lines. I am trying to make this work, but it seems to be failing miserably.
So far, I have created a transparent RichTextBox that sits on top of a panel. The Text Box is:
using System;
using System.Windows.Forms;
public class TransparentTextBox : RichTextBox
{
public TransparentTextBox()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
The paint code for the panel:
private void paper_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.White);
g.DrawLine(new Pen(Brushes.LightPink, 2), 20, 0, 20, paper.Height);
int h = TextRenderer.MeasureText("Testj", txtBody.Font).Height;
for (int x = 2 + h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
}
The lines are static, and they will grow to fit any font size/family that is chosen. The problem is when the text box is scrolled. The lines won't move with the text. I have tried to link the handle of the scroll bar to the lines, but they don't seem to be linking properly.
The code to get the current scroll position:
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO
{
public int cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
public enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
public enum ScrollInfoMask
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS
}
...
public partial class Form1 : Form
{
[DllImport("User32.dll", EntryPoint = "GetScrollInfo")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo([In]IntPtr hwnd, [In]int fnBar, [In, Out]ref SCROLLINFO lpsi);
...
private void txtBody_VScroll(object sender, EventArgs e)
{
inf.cbSize = Marshal.SizeOf(inf);
inf.fMask = (int)ScrollInfoMask.SIF_ALL;
GetScrollInfo(txtBody.Handle, 1, ref inf);
Console.WriteLine(inf.nTrackPos + ":" + inf.nPos + ":" + TextRenderer.MeasureText("Testj", txtBody.Font).Height);
paper.Invalidate();
}
Then the paint above was modified to use this:
for (int x = inf.nPos % h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
I also tried to use nTrackPos, but neither seemed to follow the text like I want it to. I'm not too familiar with C#, so I wanted to know what I am missing/could do better. I am using Visual Studio 2008, with Visual C# 2008. .Net framework 3.5 SP1
So, here is what I came up with after some intensive googling. I decided to follow more into Gusman's comment on my question and look into drawing on the textbox again. After some playing, I realized I was improperly calculating the position of the start line. So, I reconfigured my custom RichTextBox to look like:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Journal
{
class CustomRichTextBox : RichTextBox
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
private const int WM_PAINT = 0x00F;
private const int EM_GETSCROLLPOS = 0x4DD;
public int lineOffset = 0;
[DllImport("user32.dll")]
public static extern int SendMessage(
IntPtr hWnd,
int Msg,
IntPtr wParam,
ref Point lParam
);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
{
using (Graphics g = base.CreateGraphics())
{
Point p = new Point();
//get the position of the scrollbar to calculate the offset
SendMessage(this.Handle, EM_GETSCROLLPOS, IntPtr.Zero, ref p);
//draw the pink line on the side
g.DrawLine(new Pen(Brushes.LightPink, 2), 0, 0, 0, this.Height);
//determine how tall the text will be per line
int h = TextRenderer.MeasureText("Testj", this.Font).Height;
//calculate where the lines need to start
lineOffset = h - (p.Y % h);
//draw lines until there is no more box
for (int x = lineOffset; x < Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, Width, x);
}
//force the panel under us to draw itself.
Parent.Invalidate();
}
}
}
public CustomRichTextBox()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
}
I then set this box inside of a panel to get the padding I want. The panel is forced to redraw itself with the text box.

Attempted to read or write protected memory. This is often an indication that other memory is corrupt. in C++ Dll

i have a problem in code.
When i compile program so it write "AccessViolationException was unhandle"
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
C# code:
[DllImport(#"DLLmedian.dll")]
public static extern SCIOX_bitmap median(SCIOX_bitmap image, int size);
private void button2_Click(object sender, EventArgs e)
{
string url = #"img.png";
pictureBox1.Load(url);
SCIOX_bitmap a;
a.height = pictureBox1.Height;
a.width = pictureBox1.Width;
var source = new BitmapImage(new System.Uri(url));
int b = source.Format.BitsPerPixel;
a.color_depth = (4 * b) * b;
a.points = pictureBox1.Handle;
a = median(a, 8);
}
public struct SCIOX_bitmap
{
public int width;
public int height;
public int color_depth;
public IntPtr points;
};
C++ code:
SCIOX_bitmap __stdcall median(SCIOX_bitmap image, int size)
{
SCIOX_bitmap finMap;
finMap.width = image.width;
finMap.height = image.height;
finMap.color_depth = image.color_depth;
finMap.points = new int[finMap.height*finMap.width];
int *valArr = new int[size*size]
for(int i=0; i<image.width; i++)
for(int j=0; j<image.height; j++)
{
for(int k=0,n=0; k<size; k++)
for(int l=0; l<size; l++,n++)
valArr[n] = image.points[((i-size/2+k+image.width)%image.width)*image.height + (j-size/2+l+image.height)%image.height];
**//AccessViolationException was unhandle**
mysort(valArr, size*size);
if(size*size%2 == 1)
finMap.points[i*finMap.height + j] = valArr[size*size/2];
else
finMap.points[i*finMap.height + j] = (valArr[size*size/2-1] + valArr[size*size/2]) / 2;
}
return finMap;
}
struct SCIOX_bitmap{
int width;
int height;
int color_depth;
int* points;
};
Anybody help me please ?
Sorry I forgot SCIOX_bitmap is struct. SCIOX_bitmap is also in c++ code
public struct SCIOX_bitmap
{
public int width;
public int height;
public int color_depth;
public IntPtr points;
};
It appears to be that you're accessing the Handle of a PictureBox class as if it were a handle to the Image. That isn't correct, the Handle property is a handle to the Control.
If you want to get a Handle to the Image itself, you'll need to convert it to a Bitmap and then use GetHBitmap()
Bitmap bitmap = new Bitmap(pictureBox1.Image);
a.points = bitmap.GetHBitmap();
Additionally, I would recommend that you look into the Rule of Three for your C++ code, as you're going to leak the memory declared for the points buffer on each call to median.

Recording with AudioQueue and Monotouch static sound

I have written a small program in MonoTouch to record sound from the mic of my iPhone 4s using an InputAudioQueue.
I save the recorded data in an array and feed this buffer to the my audio player for playback (using OutputAudioQueue).
When playing back it's just some stuttering garbage / static sound. I have tried filling the buffer with sin waves before playback and then it sounds good, so I guess the problem is in the recording, not the playback. Can anyone help me see what is wrong? (Code below)
public class AQRecorder
{
private const int CountAudioBuffers = 3;
private const int AudioBufferLength = 22050;
private const int SampleRate = 44100;
private const int BitsPerChannel = 16;
private const int Channels = 1;
private const int MaxRecordingTime = 5;
private AudioStreamBasicDescription audioStreamDescription;
private InputAudioQueue inputQueue;
private short[] rawData;
private int indexNextRawData;
public AQRecorder ()
{
this.audioStreamDescription.Format = AudioFormatType.LinearPCM;
this.audioStreamDescription.FormatFlags = AudioFormatFlags.LinearPCMIsSignedInteger |
AudioFormatFlags.LinearPCMIsPacked;
this.audioStreamDescription.SampleRate = AQRecorder.SampleRate;
this.audioStreamDescription.BitsPerChannel = AQRecorder.BitsPerChannel;
this.audioStreamDescription.ChannelsPerFrame = AQRecorder.Channels;
this.audioStreamDescription.BytesPerFrame = (AQRecorder.BitsPerChannel / 8) * AQRecorder.Channels;
this.audioStreamDescription.FramesPerPacket = 1;
this.audioStreamDescription.BytesPerPacket = audioStreamDescription.BytesPerFrame * audioStreamDescription.FramesPerPacket;
this.audioStreamDescription.Reserved = 0;
}
public void Start ()
{
int totalBytesToRecord = this.audioStreamDescription.BytesPerFrame * AQRecorder.SampleRate * AQRecorder.MaxRecordingTime;
this.rawData = new short[totalBytesToRecord / sizeof(short)];
this.indexNextRawData = 0;
this.inputQueue = SetupInputQueue (this.audioStreamDescription);
this.inputQueue.Start ();
}
public void Stop ()
{
if (this.inputQueue.IsRunning)
{
this.inputQueue.Stop (true);
}
}
public short[] GetData ()
{
return this.rawData;;
}
private InputAudioQueue SetupInputQueue (AudioStreamBasicDescription audioStreamDescription)
{
InputAudioQueue inputQueue = new InputAudioQueue (audioStreamDescription);
for (int count = 0; count < AQRecorder.CountAudioBuffers; count++)
{
IntPtr bufferPointer;
inputQueue.AllocateBuffer(AQRecorder.AudioBufferLength, out bufferPointer);
inputQueue.EnqueueBuffer(bufferPointer, AQRecorder.AudioBufferLength, null);
}
inputQueue.InputCompleted += HandleInputCompleted;
return inputQueue;
}
private void HandleInputCompleted (object sender, InputCompletedEventArgs e)
{
unsafe
{
short* shortPtr = (short*)e.IntPtrBuffer;
for (int count = 0; count < AQRecorder.AudioBufferLength; count += sizeof(short))
{
if (indexNextRawData >= this.rawData.Length)
{
this.inputQueue.Stop (true);
return;
}
this.rawData [indexNextRawData] = *shortPtr;
indexNextRawData++;
shortPtr++;
}
}
this.inputQueue.EnqueueBuffer(e.IntPtrBuffer, AQRecorder.AudioBufferLength, null);
}
}
ok, this might be too late, but I had the same problem with hearing garbage sound only and found the solution.
You cannot read the audio data directly from e.IntPtrBuffer. This pointer is a pointer to a AudioQueueBuffer object and not to the audio data itself. So to read the audio data you can make use of the e.UnsafeBuffer which gives you the access to this object and use its AudioData pointer. This is a IntPtr which you can cast (in unsafe context) to a byte* or short* and you have your audio data.
Best regards
Alex

Taskbar location

How can i detect where the taskbar is located? I need to know for displaying my notification in the right corner. Thanks
Edit:
Thank you Hans Passant. I used that with this to get location. I hope is ok.
GetTaskbarLocation(TaskbarPosition.GetTaskbarPosition());
private void GetTaskbarLocation(Rectangle rc)
{
if (rc.X == rc.Y)
{
if (rc.Right < rc.Bottom)
taskbarLocation = TaskbarLocation.Left;
if (rc.Right > rc.Bottom)
taskbarLocation = TaskbarLocation.Top;
}
if (rc.X > rc.Y)
taskbarLocation = TaskbarLocation.Right;
if (rc.X < rc.Y)
taskbarLocation = TaskbarLocation.Bottom;
}
public static Rectangle GetTaskbarPosition() {
var data = new APPBARDATA();
data.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero) throw new Win32Exception("Please re-install Windows");
return new Rectangle(data.rc.left, data.rc.top,
data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
// P/Invoke goo:
private const int ABM_GETTASKBARPOS = 5;
[System.Runtime.InteropServices.DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref APPBARDATA data);
private struct APPBARDATA {
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private struct RECT {
public int left, top, right, bottom;
}
SHAppBarMessage(ABM_GETTASKBARPOS)
See the SHAppBarMessage Function and the ABM_GETTASKBARPOS Message for more info and the pinvoke page for SHAppBarMessage has a VB.Net sample that shouldn't be too difficult to translate.
The SHAppBarMessage function will return you information about the taskbar if you pass in the ABM_GETTASKBARPOS message. It has an out parameter which is a pointer to APPBARDATA that contains the screen cooridinates of the task bar. You can use to work out where on screen it is.
It's probably best to use the available API: NotifyIcon.ShowBalloonTip:
void Form1_DoubleClick(object sender, EventArgs e)
{
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(20000, "Information", "This is the text",
ToolTipIcon.Info );
}
In Java, using JNA (adapted from other C# solutions above)
public static Rectangle getTaskbarPosition() throws Exception {
APPBARDATA data = new APPBARDATA();
data.cbSize = new WinDef.DWORD(data.size());
WinDef.UINT_PTR retval = Shell32.INSTANCE.SHAppBarMessage(ABM_GETTASKBARPOS, data);
if (retval == null) {
throw new Exception("Please re-install Windows");
}
return new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}

Categories