C++ CLI lambda expression with handle to managed object - c++-cli

I'm trying to convert he following C# code to C++/CLI
// create the output bitmap
var bitmap = new SKBitmap();
SKImageInfo info = new SKImageInfo();
info = new SKImageInfo(input.Width, input.Height, SKColorType.Bgra8888);
// create the buffer that will hold the pixels
var buffer = new byte[input.Width * input.Height * 4];
// get a pointer to the buffer, and give it to the bitmap
var outputPtr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
bitmap.InstallPixels(info, outputPtr.AddrOfPinnedObject(), info.RowBytes, (addr, ctx) => outputPtr.Free(), null);
I've tried different options and but I keep getting stuck. I now have this:
static void FreeHandle(System::Runtime::InteropServices::GCHandle handle)
{
handle.Free();
}
static void Test()
{
// create the output bitmap
SKBitmap^ bitmap = gcnew SKBitmap();
SKImageInfo^ info = gcnew SKImageInfo(input.cols, input.rows, SKColorType::Bgra8888);
// Create a buffer, pin it and give its pointer to the bitmap
array<System::Byte>^ buffer = gcnew array<System::Byte>(input.cols * input.rows * 4);
GCHandle outputPtr = GCHandle::Alloc(buffer, GCHandleType::Pinned);
bitmap->InstallPixels(info, outputPtr.AddrOfPinnedObject(), info->RowBytes, gcnew Action<GCHandle>(outputPtr, &FreeHandle), nullptr);
}
This gives me the error
invalid delegate initializer -- object is not needed for the specified function.
I also tried:
// create the output bitmap
SKBitmap^ bitmap = gcnew SKBitmap();
SKImageInfo^ info = gcnew SKImageInfo(input.cols, input.rows, SKColorType::Bgra8888);
// Create a buffer, pin it and give its pointer to the bitmap
array<System::Byte>^ buffer = gcnew array<System::Byte>(input.cols * input.rows * 4);
gcroot<GCHandle> outputPtr = GCHandle::Alloc(buffer, GCHandleType::Pinned);
bitmap->InstallPixels(info, outputPtr.AddrOfPinnedObject(), info->RowBytes, [&]() -> void { outputPtr->Free(); }, nullptr);
but then I get:
expression must have pointer or handle type but it has type "System::Runtime::InteropServices::GCHandle".
I searched a lot, but I'm not sure how to search this anymore.

Related

Type of expression is ambiguous without more context

I'm trying to convert the code of objective C mentioned here - https://gist.github.com/Coeur/1409855/f6df10c79f8cdd0fcb2a0735b99f4b3a74b9f954
to swift
The code I've wrote till now in swift -
class func getMacAddress(_ ifName: String?) -> String? {
var mgmtInfoBase = [Int32](repeating: 0, count: 6)
var msgBuffer: Int8? = nil
var length: size_t
var macAddress = [UInt8](repeating: 0, count: 6)
var interfaceMsgStruct: if_msghdr?
var socketStruct: sockaddr_dl?
var errorFlag: String? = nil
// Setup the management Information Base (mib)
mgmtInfoBase[0] = Int32(Int(CTL_NET)) // Request network subsystem
mgmtInfoBase[1] = Int32(Int(AF_ROUTE)) // Routing table info
mgmtInfoBase[2] = 0
mgmtInfoBase[3] = Int32(Int(AF_LINK)) // Request link layer information
mgmtInfoBase[4] = Int32(Int(NET_RT_IFLIST)) // Request all configured interfaces
mgmtInfoBase[5] = Int32(if_nametoindex(ifName?.utf8CString)) //ERROR: Type of expression is ambiguous without more context
// With all configured interfaces requested, get handle index
if ( mgmtInfoBase[5] == 0) {
errorFlag = "if_nametoindex failure"
} else {
// Get the size of the data available (store in len)
if sysctl(&mgmtInfoBase, 6, nil, &length, nil, 0) < 0 {
errorFlag = "sysctl mgmtInfoBase failure"
} else {
// Alloc memory based on above call
if (msgBuffer = Int8((length))) == nil {
errorFlag = "buffer allocation failure"
} else {
// Get system information, store in buffer
if sysctl(&mgmtInfoBase, 6, &msgBuffer, &length, nil, 0) < 0 {
errorFlag = "sysctl msgBuffer failure"
}
}
}
}
// Before going any further...
if errorFlag != nil {
// Release the buffer memory
if (msgBuffer != nil) {
free(&msgBuffer)
}
return nil
}
// Map msgbuffer to interface message structure
interfaceMsgStruct = msgBuffer as? if_msghdr
// Map to link-level socket structure
socketStruct = (interfaceMsgStruct + 1) as? sockaddr_dl // ERROR: Cannot convert value of type 'if_msghdr?' to expected argument type 'Int'
// Copy link layer address data in socket structure to an array
if socketStruct == nil {
return nil
}
memcpy(&macAddress, socketStruct.sdl_data + socketStruct.sdl_nlen, 6) // ERROR: Type of expression is ambiguous without more context
// Read from char array into a string object, into traditional Mac address format
let macAddressString = String(format: "%02X:%02X:%02X:%02X:%02X:%02X", macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5])
// Release the buffer memory
free(&msgBuffer)
return macAddressString
}
I'm getting the errors that I've mentioned. I searched and tried every possible thing and read articles from the documentation but still I couldn't get away with these errors. Please help.
The definitions of the functions for which I'm getting error as mentioned in Darwin.posix.net.if -
public func if_nametoindex(_: UnsafePointer<Int8>!) -> UInt32
public struct if_msghdr {
public var ifm_msglen: UInt16 /* to skip non-understood messages */
public var ifm_version: UInt8 /* future binary compatability */
public var ifm_type: UInt8 /* message type */
public var ifm_addrs: Int32 /* like rtm_addrs */
public var ifm_flags: Int32 /* value of if_flags */
public var ifm_index: UInt16 /* index for associated ifp */
public var ifm_data: if_data /* statistics and other data about if */
public init()
public init(ifm_msglen: UInt16, ifm_version: UInt8, ifm_type: UInt8, ifm_addrs: Int32, ifm_flags: Int32, ifm_index: UInt16, ifm_data: if_data)
}
public func memcpy(_ __dst: UnsafeMutableRawPointer!, _ __src: UnsafeRawPointer!, _ __n: Int) -> UnsafeMutableRawPointer!````

Canon EDSDK.EdsGetImage error EDS_ERR_NOT_SUPPORTED when reading CR2 file

I'm trying to read a CR2 file using the Canon EDSDKv0309W. I didn't found an example for this SDK version so I looked at several examples from older versions and created the code below. But I always get EDS_ERR_NOT_SUPPORTED in line EDSDK.EdsGetImage(..).
Using a 32Bit compilation under .Net4.6.1 I can read the correct with and height from images taken with EOS500D and M100. But I don't get the image. So my assumption is I get a wrong pointer from EdsCreateMemoryStream. But I don't see what is wrong and how to debug this.
Any help will be appreciated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EDSDKLib;
using System.Drawing;
using System.Drawing.Imaging;
namespace CR2Reader
{
class Program
{
static Bitmap GetImage(IntPtr img_stream, EDSDK.EdsImageSource imageSource)
{
IntPtr stream = IntPtr.Zero;
IntPtr img_ref = IntPtr.Zero;
IntPtr streamPointer = IntPtr.Zero;
EDSDK.EdsImageInfo imageInfo;
uint error = 0;
try
{
//create reference and get image info
error = EDSDK.EdsCreateImageRef(img_stream, out img_ref);
if (error == 0)
{
error = EDSDK.EdsGetImageInfo(img_ref, imageSource, out imageInfo);
if (error == 0)
{
EDSDK.EdsSize outputSize = new EDSDK.EdsSize();
outputSize.width = imageInfo.EffectiveRect.width;
outputSize.height = imageInfo.EffectiveRect.height;
//calculate amount of data
int datalength = outputSize.height * outputSize.width * (int)imageInfo.NumOfComponents * (int)(imageInfo.ComponentDepth / 8);
//create buffer that stores the image
error = EDSDK.EdsCreateMemoryStream((ulong)datalength, out stream);
if (error == 0)
{
//load image into the buffer
error = EDSDK.EdsGetImage(img_ref, imageSource, EDSDK.EdsTargetImageType.RGB16, imageInfo.EffectiveRect, outputSize, stream);
if (error == 0)
{
//make BGR from RGB (System.Drawing (i.e. GDI+) uses BGR)
byte[] buffer = new byte[datalength];
unsafe
{
System.Runtime.InteropServices.Marshal.Copy(stream, buffer, 0, datalength);
byte tmp;
fixed (byte* pix = buffer)
{
for (int i = 0; i < datalength; i += 3)
{
tmp = pix[i]; //Save B value
pix[i] = pix[i + 2]; //Set B value with R value
pix[i + 2] = tmp; //Set R value with B value
}
}
}
//Get pointer to stream
error = EDSDK.EdsGetPointer(stream, out streamPointer);
if (error == 0)
{
//Create bitmap with the data in the buffer
return new Bitmap(outputSize.width, outputSize.height, datalength, PixelFormat.Format24bppRgb, streamPointer);
}
}
}
}
}
return null;
}
finally
{
//Release all data
if (img_ref != IntPtr.Zero) error = EDSDK.EdsRelease(img_ref);
if (stream != IntPtr.Zero) error = EDSDK.EdsRelease(stream);
}
}
static Bitmap ReadCR2Image(string fileName)
{
IntPtr outStream = new IntPtr();
uint error = EDSDK.EdsInitializeSDK();
error += EDSDK.EdsCreateFileStream(fileName,
EDSDK.EdsFileCreateDisposition.OpenExisting,
EDSDK.EdsAccess.Read,
out outStream);
Bitmap bmp = null;
if (error == 0)
{
bmp = GetImage(outStream, EDSDK.EdsImageSource.FullView);
}
if (outStream != IntPtr.Zero)
{
error = EDSDK.EdsRelease(outStream);
}
EDSDK.EdsTerminateSDK();
return bmp;
}
static void Main(string[] args)
{
Bitmap bmp = ReadCR2Image("IMG_3113.CR2");
}
}
}
You are using the wrong EdsImageSource type.
Since you are loading a RAW image you also have to use EdsImageSource.RAWFullView. EdsImageSource.FullView would be appropriate for e.g. a JPG or TIFF.
Once you change that, everything should work just fine.
Edit: just saw you are using RGB16 as target but the rest of the code assumes normal 8bit RGB. You'll have to change a whole bunch of stuff to get that working correctly. I'd advise you to use RGB unless you really need 16bit.
Edit 2: Looks like the library is a bit out of that in that regard (I should really update it). In any case, you can always check the header files of the SDK for up-to-date values. Here's the current definition for EdsImageSource:
enum EdsImageSource
{
FullView = 0,
Thumbnail,
Preview,
RAWThumbnail,
RAWFullView,
}
As for changes required for 16bit:
datalength is incorrect
you're using byte instead of ushort to set the pixels
you create the Bitmap with PixelFormat.Format24bppRgb
then there's another thing where Bitmap doesn't fully support 16Bit. See this article for some in-depth information.
Depending on what you need to do it's probably better to either use the raw pixel data directly as you get it from the SDK or use a different graphics library (e.g. WPF, SkiaSharp, ImageSharp, etc.)

Saved joints from kinect skeleton track

I work with the kinect. My goal is to store the values ​​gives me the kinect for the location of the body (head,hand etc). I have written some code but I can not understand what values ​​should save and how.I want to store in the db or in a txt file the position of the head,hands and foots.I want the data to understand the movements of the person who stand in front of kinect.For example if someone move her hand the kinect will sent a value.I must store it and understand and to understand the move taken.Sorry for the few info
here is my code:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Kinect;
using System.Linq;
using System.IO;
namespace KinectSkeletonApplication1
{
public partial class MainWindow : Window
{
//Instantiate the Kinect runtime. Required to initialize the device.
//IMPORTANT NOTE: You can pass the device ID here, in case more than one Kinect device is connected.
KinectSensor sensor = KinectSensor.KinectSensors[0];
byte[] pixelData;
Skeleton[] skeletons;
public MainWindow()
{
InitializeComponent();
///////////////////////////////////
///////////////////////////////
//Runtime initialization is handled when the window is opened. When the window
//is closed, the runtime MUST be unitialized.
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
this.Unloaded += new RoutedEventHandler(MainWindow_Unloaded);
sensor.ColorStream.Enable();
sensor.SkeletonStream.Enable();
}
void runtime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
bool receivedData = false;
using (SkeletonFrame SFrame = e.OpenSkeletonFrame())
{
if (SFrame == null)
{
// The image processing took too long. More than 2 frames behind.
}
else
{
skeletons = new Skeleton[SFrame.SkeletonArrayLength];
SFrame.CopySkeletonDataTo(skeletons);
receivedData = true;
}
}
if (receivedData)
{
Skeleton currentSkeleton = (from s in skeletons
where s.TrackingState == SkeletonTrackingState.Tracked
select s).FirstOrDefault();
if (currentSkeleton != null)
{
SetEllipsePosition(head, currentSkeleton.Joints[JointType.Head]);
SetEllipsePosition(leftHand, currentSkeleton.Joints[JointType.HandLeft]);
SetEllipsePosition(rightHand, currentSkeleton.Joints[JointType.HandRight]);
SetEllipsePosition(shoulder_center, currentSkeleton.Joints[JointType.ShoulderCenter]);
}
}
}
//This method is used to position the ellipses on the canvas
//according to correct movements of the tracked joints.
//IMPORTANT NOTE: Code for vector scaling was imported from the Coding4Fun Kinect Toolkit
//available here: http://c4fkinect.codeplex.com/
//I only used this part to avoid adding an extra reference.
private void SetEllipsePosition(Ellipse ellipse, Joint joint)
{
Microsoft.Kinect.SkeletonPoint vector = new Microsoft.Kinect.SkeletonPoint();
vector.X = ScaleVector(640, joint.Position.X);
vector.Y = ScaleVector(480, -joint.Position.Y);
vector.Z = joint.Position.Z;
Joint updatedJoint = new Joint();
updatedJoint = joint;
updatedJoint.TrackingState = JointTrackingState.Tracked;
updatedJoint.Position = vector;
Canvas.SetLeft(ellipse, updatedJoint.Position.X);
Canvas.SetTop(ellipse, updatedJoint.Position.Y);
}
private float ScaleVector(int length, float position)
{
float value = (((((float)length) / 1f) / 2f) * position) + (length / 2);
if (value > length)
{
return (float)length;
}
if (value < 0f)
{
return 0f;
}
string r = Convert.ToString(value);
string path = #"C:\Test\MyTest.txt";
// This text is added only once to the file.
if (!File.Exists(path))
{
// Create a file to write to.
string createText = "Hello and Welcome" + Environment.NewLine;
File.WriteAllText(path, createText);
}
// This text is always added, making the file longer over time
// if it is not deleted.
//string appendText = "This is extra text" + Environment.NewLine;
File.AppendAllText(path, r);
// Open the file to read from.
string readText = File.ReadAllText(path);
return value;
}
void MainWindow_Unloaded(object sender, RoutedEventArgs e)
{
sensor.Stop();
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
sensor.SkeletonFrameReady += runtime_SkeletonFrameReady;
sensor.ColorFrameReady += runtime_VideoFrameReady;
sensor.Start();
}
void runtime_VideoFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
bool receivedData = false;
using (ColorImageFrame CFrame = e.OpenColorImageFrame())
{
if (CFrame == null)
{
// The image processing took too long. More than 2 frames behind.
}
else
{
pixelData = new byte[CFrame.PixelDataLength];
CFrame.CopyPixelDataTo(pixelData);
receivedData = true;
}
}
if (receivedData)
{
BitmapSource source = BitmapSource.Create(640, 480, 96, 96,
PixelFormats.Bgr32, null, pixelData, 640 * 4);
videoImage.Source = source;
}
}
}
}
I think what you are looking for is getting the X, Y, Z coordiantes for certain Joints?
In this case add this code:
Vector3D ShoulderCenter = new Vector3D(skeleton.Joints[JointType.ShoulderCenter].Position.X, skeleton.Joints[JointType.ShoulderCenter].Position.Y, skeleton.Joints[JointType.ShoulderCenter].Position.Z);
Vector3D RightShoulder = new Vector3D(skeleton.Joints[JointType.ShoulderRight].Position.X, skeleton.Joints[JointType.ShoulderRight].Position.Y, skeleton.Joints[JointType.ShoulderRight].Position.Z);
Vector3D LeftShoulder = new Vector3D(skeleton.Joints[JointType.ShoulderLeft].Position.X, skeleton.Joints[JointType.ShoulderLeft].Position.Y, skeleton.Joints[JointType.ShoulderLeft].Position.Z);
Vector3D RightElbow = new Vector3D(skeleton.Joints[JointType.ElbowRight].Position.X, skeleton.Joints[JointType.ElbowRight].Position.Y, skeleton.Joints[JointType.ElbowRight].Position.Z);
Vector3D LeftElbow = new Vector3D(skeleton.Joints[JointType.ElbowLeft].Position.X, skeleton.Joints[JointType.ElbowLeft].Position.Y, skeleton.Joints[JointType.ElbowLeft].Position.Z);
Vector3D RightWrist = new Vector3D(skeleton.Joints[JointType.WristRight].Position.X, skeleton.Joints[JointType.WristRight].Position.Y, skeleton.Joints[JointType.WristRight].Position.Z);
Vector3D LeftWrist = new Vector3D(skeleton.Joints[JointType.WristLeft].Position.X, skeleton.Joints[JointType.WristLeft].Position.Y, skeleton.Joints[JointType.WristLeft].Position.Z);
This only defines the Vectors for the upper Body part as you can see. Just add the missing joints the way I did it.
You need following assemblies:
using System.Windows.Media;
using Microsoft.Kinect.Toolkit.Fusion;
using System.Windows.Media.Media3D;

How to create a single-color Bitmap to display a given hue?

I have a requirement to create an image based on a certain color. The color will vary and so will the size of the output image. I want to create the Bitmap and save it to the app's temporary folder. How do I do this?
My initial requirement came from a list of colors, and providing a sample of the color in the UI. If the size of the image is variable then I can create them for certain scenarios like result suggestions in the search pane.
This isn't easy. But it's all wrapped in this single method for you to use. I hope it helps. ANyway, here's the code to create a Bitmap based on a given color & size:
private async System.Threading.Tasks.Task<Windows.Storage.StorageFile> CreateThumb(Windows.UI.Color color, Windows.Foundation.Size size)
{
// create colored bitmap
var _Bitmap = new Windows.UI.Xaml.Media.Imaging.WriteableBitmap((int)size.Width, (int)size.Height);
byte[] _Pixels = new byte[4 * _Bitmap.PixelWidth * _Bitmap.PixelHeight];
for (int i = 0; i < _Pixels.Length; i += 4)
{
_Pixels[i + 0] = color.B;
_Pixels[i + 1] = color.G;
_Pixels[i + 2] = color.R;
_Pixels[i + 3] = color.A;
}
// update bitmap data
// using System.Runtime.InteropServices.WindowsRuntime;
using (var _Stream = _Bitmap.PixelBuffer.AsStream())
{
_Stream.Seek(0, SeekOrigin.Begin);
_Stream.Write(_Pixels, 0, _Pixels.Length);
_Bitmap.Invalidate();
}
// determine destination
var _Folder = Windows.Storage.ApplicationData.Current.TemporaryFolder;
var _Name = color.ToString().TrimStart('#') + ".png";
// use existing if already
Windows.Storage.StorageFile _File;
try { return await _Folder.GetFileAsync(_Name); }
catch { /* do nothing; not found */ }
_File = await _Folder.CreateFileAsync(_Name, Windows.Storage.CreationCollisionOption.ReplaceExisting);
// extract stream to write
// using System.Runtime.InteropServices.WindowsRuntime;
using (var _Stream = _Bitmap.PixelBuffer.AsStream())
{
_Pixels = new byte[(uint)_Stream.Length];
await _Stream.ReadAsync(_Pixels, 0, _Pixels.Length);
}
// write file
using (var _WriteStream = await _File.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
var _Encoder = await Windows.Graphics.Imaging.BitmapEncoder
.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId, _WriteStream);
_Encoder.SetPixelData(Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,
Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied,
(uint)_Bitmap.PixelWidth, (uint)_Bitmap.PixelHeight, 96, 96, _Pixels);
await _Encoder.FlushAsync();
using (var outputStream = _WriteStream.GetOutputStreamAt(0))
await outputStream.FlushAsync();
}
return _File;
}
Best of luck!

WINCODEC_ERR_WIN32ERROR 0x88982F94 when calling IWICComponentFactory.CreateBitmapFromMemory

I'm getting the following error when calling IWICComponentFactory.CreateBitmapFromMemory and passing it a pointer to Scan0 of a 32bppArgb GDI+ bitmap
WINCODEC_ERR_WIN32ERROR
0x88982F94
Windows Codecs received an error from the Win32 system.
IWICComponentFactory interface decl:
new IWICBitmap CreateBitmapFromMemory(
uint uiWidth,
uint uiHeight,
[MarshalAs(UnmanagedType.LPStruct)]
Guid pixelFormat,
uint cbStride,
uint cbBufferSize,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)]
byte[] pbBuffer
);
new IWICBitmap CreateBitmapFromMemory(
uint uiWidth,
uint uiHeight,
[MarshalAs(UnmanagedType.LPStruct)]
Guid pixelFormat,
uint cbStride,
uint cbBufferSize,
IntPtr pbBuffer
);
Full code:
public static IWICBitmap ToWic(IWICComponentFactory factory, Bitmap bit) {
BitmapData bd = bit.LockBits(new Rectangle(0, 0, bit.Width, bit.Height),
ImageLockMode.ReadOnly, bit.PixelFormat);
IWICBitmap b = null;
try {
//Create WIC bitmap directly from unmanaged memory
b = factory.CreateBitmapFromMemory((uint)bit.Width, (uint)bit.Height,
ConversionUtils.FromPixelFormat(bit.PixelFormat), (uint)bd.Stride,
(uint)(bd.Stride * bd.Height), bd.Scan0);
return b;
} finally {
bit.UnlockBits(bd);
}
}
Width, Height, buffer size, format GUID, and scan size all seem correct. I can't figure out why this is happening (there are no google results for the error code or message
This isn't an answer as to why the original code doesn't work - but it's a workaround. Using IWICImagingFactory_CreateBitmapFromMemory_Proxy , everything works fine. But why didn't the original work as it's supposed to? And why the _Proxy methods with near-identical signatures?
[DllImport("WindowsCodecs.dll", EntryPoint = "IWICImagingFactory_CreateBitmapFromMemory_Proxy")]
internal static extern int CreateBitmapFromMemory(IWICComponentFactory factory, uint width, uint height, ref Guid pixelFormatGuid, uint stride, uint cbBufferSize, IntPtr pvPixels, out IWICBitmap ppIBitmap);
public static IWICBitmap ToWic(IWICComponentFactory factory, Bitmap bit) {
Guid pixelFormat = ConversionUtils.FromPixelFormat(bit.PixelFormat);
if (pixelFormat == Guid.Empty) throw new NotSupportedException("PixelFormat " + bit.PixelFormat.ToString() + " not supported.");
BitmapData bd = bit.LockBits(new Rectangle(0, 0, bit.Width, bit.Height), ImageLockMode.ReadOnly, bit.PixelFormat);
IWICBitmap b = null;
try {
//Create WIC bitmap directly from unmanaged memory
long result = CreateBitmapFromMemory(factory, (uint)bit.Width,
(uint)bit.Height, ref pixelFormat, (uint)bd.Stride,
(uint)(bd.Stride * bd.Height), bd.Scan0, out b);
if (result == 0x80070057) throw new ArgumentException();
if (result < 0) throw new Exception("HRESULT " + result);
return b;
} finally {
bit.UnlockBits(bd);
}
}
For reference, here is the COM method and here is the proxy method. Both use [IN] BYTE *pbBuffer.