0

I'm working on a (Universal Windows) c++/cx Directx project, which builds to a dll used in a c# UWP project.

I'm using the DirectX Toolkit to load textures.

I already use it to create a texture from file, but now I need it to create a texture from a byte array that was send from the UWP project. But when trying to use CreateWICTextureFromMemory(), the HRESULT says 0x88982F50:"The component cannot be found"

All I can find about this problem indicates the bytes are not a correct image, but I tested it in the UWP project, there I get the byte array from bingmaps (it's a static map image), and I could make a working image from these bytes.

Does annyone know what I'm doing wrong?

UWP c# download code (to get the bytes):

private async Task DownloadTexture()
    {
        byte[] buffer = null;
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
            WebResponse response = await request.GetResponseAsync();
            using (Stream stream = response.GetResponseStream())
            using (MemoryStream ms = new MemoryStream())
            {
                stream.CopyTo(ms);
                buffer = ms.ToArray();
            }
        }
        catch (Exception exception)
        {
            Logger.Error($"Could not Download Texture: {exception}");
        }

        _track3D.SetImage(out buffer[0], (ulong)buffer.Length);
    }

Directx C++ code (that fails):

void Track3D::SetImage(uint8_t* ddsData, size_t ddsDataSize)
{
    HRESULT result = CreateWICTextureFromMemory(_d3dDevice.Get(), _d3dContext.Get(), ddsData, ddsDataSize, nullptr, _Terrain.ReleaseAndGetAddressOf());
    //here it goes wrong
    //TODO: use the texture
}

UWP C# test code that works (displays image):

private async void setImage(byte[] buffer) //test
    {
        try
        {
            BitmapImage bmpImage = new BitmapImage();
            using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
            {
                await stream.WriteAsync(buffer.AsBuffer());
                stream.Seek(0);
                await bmpImage.SetSourceAsync(stream);
            }

            Image image = new Image();
            image.Source = bmpImage;

            ((Grid)Content).Children.Add(image);
        }
        catch (Exception exception)
        {
            Logger.Error($"{exception}");
        }
    }

EDIT:

OK, turns out the first byte in the buffer is different in the C++ code, than it was when sent from UWP. When I change that first byte to the correct value in the C++ code (as a test), the texture is correctly created.

Which raises the question, why did the value of the first byte change? (Or what did I do wrong?)

As requested, The function setImage() looks like this in c#:

[MethodImpl]
public void __ITrack3DPublicNonVirtuals.SetImage(out byte ddsData, [In] ulong ddsDataSize);

(also, I just realised the parameter names still have 'dds' in their name, sorry about that, will change that in my code as it is misleading)

Stef
  • 315
  • 3
  • 14
  • 1
    Can you confirm that COM is initialized on the thread that you are calling ``CreateWICTextureFromMemory`` on? Also make sure you are using a recent release of DirectXTK as it fixed some WIC factory race-condition issues for multi-threaded use. Finally, remember that immediate context use is not thread-safe. You can use the version that doesn't take it unless you need auto-gen mipmaps, otherwise you need a mutex. – Chuck Walbourn Dec 06 '16 at 22:29
  • I'm afraid I'm not familiar with what you speak of. I'm new to both c++/cx, directx, and the directx toolkit. I've seen "COM" be mentioned quite a lot before, but I still don't really know what exactly it is. I found https://msdn.microsoft.com/en-us/library/ms694363(VS.85).aspx but it's still not clear to me. I assume this here:https://directxtk.codeplex.com/wikipage?title=WICTextureLoader (regarding CoInitialize and stuff) refers to the same thing you speak of. But I'm clueless. – Stef Dec 08 '16 at 09:19
  • Also, I was using the directxtk_uwp nuget package version 2016.10.6.1, and I just updated to 2016.12.5.1. (But that didn't change anything.) – Stef Dec 08 '16 at 09:57
  • Basically I don't know how to initialize COM (I don't seem to have those functions available, and I don't know what to include/reference to have them), and I don't know what \initializing COM exactly means. (btw, sorry for the triple posts, but I'm just not able to edit these previous comments anymore for some reason?) – Stef Dec 08 '16 at 10:13
  • OK, I found out the problem, it seems the first byte in the buffer is different in the C++ directx code, than it was sent from UWP. When I change that first byte to the correct value (as a test) in the C++ code, the texture is correctly created. Which raises the question, why did the value change? – Stef Dec 08 '16 at 13:42
  • Can you update your post with details on the C# definition of your native code method here? – Chuck Walbourn Dec 09 '16 at 17:25
  • sure, I added it at the bottom of my question – Stef Dec 12 '16 at 13:33

1 Answers1

1

0x88982F50: “The component cannot be found”

This is WINCODEC_ERR_COMPONENTNOTFOUND which happens whenever WIC can't determine what format codec to use for a file/binary. Your problem is your transfer of the data from managed to native code is wrong.

Your interop method is set to:

[MethodImpl]
public void __ITrack3DPublicNonVirtuals.SetImage(out byte ddsData, [In] ulong ddsDataSize);

With the C++ method signature being:

void Track3D::SetImage(uint8_t* ddsData, size_t ddsDataSize)

Because of the out your first parameter is being passed as a safe array with the length in the first element.

Instead you should use:

SetImage([In] byte ddsData, [In] ulong ddsDataSize); // C#

void Track3D::SetImage(const uint8_t* ddsData, size_t ddsDataSize); // C++.
Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81
  • Initialy I thought as well that I had to add "const", but when I do that I get "Error C4400 'const uint8_t': const/volatile qualifiers on this type are not supported". Also, the c# method signature isn't written by me or anyone, it's just automatically created/interpreted that way or something by the UWP project. But I agree, that "out" seems wrong there. – Stef Dec 13 '16 at 14:15
  • sigh, I feel like I keep making the same mistakes over and over again, because I think I had this same problem a while ago. I think perhaps I could send the array as "const Platform::Array^" instead of "uint8_t*". Would that work? – Stef Dec 13 '16 at 14:23
  • Yup, I'm an idiot. I had this exact same problem a month ago. If I do it the way I just described, the "out" turns into an "[In]" on the c# side. I really should have seen that "out" there and realised that was wrong. Sorry for the trouble, and thanks for the help. (I still don't get why sending it the way I did caused the first byte to be incorrect, but all the others to be correct though.) – Stef Dec 13 '16 at 14:32
  • I think it was getting marshalled as a 'safe array' which provides the length in the first element. – Chuck Walbourn Dec 13 '16 at 17:17
  • Ah, well that would explain. Thanks for your help. (btw, if you want, you could add these things we said to your answer, I'll make it the accepted answer then) – Stef Dec 16 '16 at 11:29
  • I see you updated your answer, but actually that results in an error as you can't use const for that type: "Error C4400 'const uint8_t': const/volatile qualifiers on this type are not supported". That's why I used "const Platform::Array^" instead. – Stef Dec 19 '16 at 09:22