0

I'm implementing a Custom Credential Provider in C#. I'm using a C++ project as example. This piece of C++ code provides an image to Windows. The way I see it phbmp is a pointer to the image-bitmap. The code either updates the pointer so it points to a new bitmap (read from Resource) or it loads the bitmap to the address pointed by phbmp. I'm not sure if the pointer itself is changed or not.

// Get the image to show in the user tile
HRESULT CSampleCredential::GetBitmapValue(DWORD dwFieldID, _Outptr_result_nullonfailure_ HBITMAP *phbmp)
{
    HRESULT hr;
    *phbmp = nullptr;

    if ((SFI_TILEIMAGE == dwFieldID))
    {
        HBITMAP hbmp = LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_TILE_IMAGE));
        if (hbmp != nullptr)
        {
            hr = S_OK;
            *phbmp = hbmp;
        }
        else
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

Below is the C# equivalent I'm implementing:

public int GetBitmapValue(uint dwFieldID, IntPtr phbmp)
{
    if (dwFieldID == 2)
    {
        Bitmap image = Resource1.TileImage;

        ImageConverter imageConverter = new ImageConverter();
        byte[] bytes = (byte[])imageConverter.ConvertTo(image, typeof(byte[]));

        Marshal.Copy(bytes, 0, phbmp, bytes.Length);

        return HResultValues.S_OK;
    }

    return HResultValues.E_INVALIDARG;
}

What I'm trying to do:

  1. Load the image from resource (this works, it has the correct length)
  2. Convert the Bitmap to an array of bytes
  3. Copy these bytes to the address pointed by phbmp

This crashes, I assume because of memory-allocation.

The parameters in this method are defined by an interface (in CredentialProvider.Interop.dll, which is provided by Microsoft - I think). So I'm pretty sure it's correct and phbmp is not an out-parameter.

Because it is not an out-parameter I can not change phbmp to let it point to my bitmap, right? I have assigned phbmp to Bitmap.GetHbitmap() and that doesn't crash but it isn't working either. I assume that the change to phbmp is only local in this method.

I can understand that it is not possible to alloc memory to a predefined address. It's the other way around: you alloc memory and get an pointer to it. But then this change is local again. How does this work?

roberth
  • 924
  • 9
  • 17
  • An HBITMAP is an opaque bitmap handle. You can think of a handle as a magic integer that GDI apis can use to lookup the bitmap data from a table. An `HBITMAP*` is then a pointer to a handle. Your C# code has no talk of handles. – Yakk - Adam Nevraumont Dec 15 '17 at 15:57
  • https://stackoverflow.com/questions/16092696/windows-credential-provider-with-c-sharp – aybe Dec 16 '17 at 06:26
  • @Yakk, do you think that the IntPtr in C# should be an out-parameter? I think it should to match the functionality in C++. The problem is not to get a pointer to the Bitmap (I think). Maybe the way I have done that in the C#-code is incorrect but I can get that right. – roberth Dec 17 '17 at 08:20
  • @roberth `IntPtr` is a pointer to some data. You have to write where it is pointing, but you do not change the pointer. – Yakk - Adam Nevraumont Dec 17 '17 at 08:45
  • @Yakk, thank you for your help. So I assume there is memory available at the address the pointer points to? But how much? They don't know the size of my image, right? And I cannot allocate memory *on* that address because why would there be space available on that address? If I just allocate memory the size of my image, I get a new pointer to that space. if I assign that pointer to IntPtr I have changed IntPtr and that will not propagate back to the calling function because it's not an out-parameter..... I don't understand how it works. – roberth Dec 17 '17 at 13:36
  • @roberth "An HBITMAP is an opaque bitmap handle. You can think of a handle as a magic integer that GDI apis can use to lookup the bitmap data from a table." That was the very first thing I said to you. You need to put an HBITMAP "magic integer" into the `IntPtr`, not a bitmap. How you get that magic integer, well, I'd write C++ code myself. But you seem to want to write C#, so you get to fight with the .net runtime and libraries to figure it out. – Yakk - Adam Nevraumont Dec 17 '17 at 15:20
  • Thank you @Yakk, I did so with Bitmap.GetHbitmap() as this example shows (https://msdn.microsoft.com/en-us/library/1dz311e4(v=vs.110).aspx). But because this didn't work (sorry I'm unable to say any clearer what goes wrong) I assumed I was changing the IntPtr in the local context of this function only. But with your help I now know in which direction I have to think. – roberth Dec 18 '17 at 08:53

1 Answers1

1

Although some people agreed that IntPtr should be an out-parameter (see comments in https://syfuhs.net/2017/10/15/creating-custom-windows-credential-providers-in-net/) the answer was actually:

var bmp = new Bitmap(imageStream);  
Marshal.WriteIntPtr(phbmp, bmp.GetHbitmap()); 
roberth
  • 924
  • 9
  • 17