locked
Why does the first character of wchar_t* gets erased when passed to a WinRT component?

    Question

  • I have a ref class in a WinRT component:

    namespace WinRTComponent
    {
    	public ref class Class1 sealed
    	{
    	public:
    		Class1();
    
    		void MyMethod(wchar_t* wcharPtr)
    		{
    			// Do nothing.
    		}
    	};
    }

    I also have a Windows Store C++ XAML app, which has reference to the WinRT component. In my app I run the following code:

    std::wstring str = L"Some text.";
    const wchar_t* strPtr = str.data();
    
    WinRTComponent::Class1^ class1 = ref new WinRTComponent::Class1();
    
    wchar_t firstCharBefore = strPtr[0]; // It is 'S', correctly.
    
    class1->MyMethod(const_cast<wchar_t*>(strPtr));
    
    wchar_t firstCharAfter = strPtr[0]; // It is 0! Why?

    When I pass my wchar_t* pointer to the public method of the WinRT component, the first character of the string gets erased and is changed to 0.

    What is the reason for this? Is this expected behaviour or a bug?

    • Edited by MarkVincze Thursday, June 13, 2013 10:51 AM
    Thursday, June 13, 2013 10:49 AM

Answers

  • In string params should be const. you should also encode other parts of the specified behavior using SAL.

    _In_ _Nullterminated_ wchar_t const *

    PCWSTR is most of that, so

    _In_ PCWSTR

    COM rules say out params must be stabilized to zero. if there is a marshaling layer in between it might be implementing that for you.

    using Platform::String does not require a copy in thus case, a string reference avoids that.

    so use Platform::String and pass your wstring directly, the compiler will do the implicit conversion to HSTRING (Platform::String) via a string reference.

    Chris

    Sunday, June 16, 2013 5:30 PM

All replies

  • I think WinRT components require the use of Platform::String^ for passing string variables.

    See Strings(C++/CX).

    Thursday, June 13, 2013 11:04 AM
  • And is there any specific reason for that?

    I have a rather long string (thousands of characters), and I store that as an std::wstring internally. I would like to pass a wchar_t* pointer pointing to a certain part of this long string to a WinRT component, and I'd like to avoid copying.

    The documentation suggests that a StringReference should be used in situations like this, but that is a native C++ type, thus it can't be used in the signature of a public method in a WinRT component.

    Thursday, June 13, 2013 11:39 AM
  • I also tried it like this:

    std::wstring str = L"Some text.";
    
    std::vector<wchar_t> strCpy(str.begin(), str.end());
    
    class1->MyMethod(&strCpy[0]);

    And the same thing happens, the first element of the vector is changed to 0.

    This isn't normal behaviour, I guess?

    Friday, June 14, 2013 7:39 AM
  • And the Class1 class was in a separate WinRT component, and the Windows Store app had reference to it? So you didn't simply include the header of Class1 and built the two projects together, right?

    Then this behaviour also depends on some machine characteristics or compiler settings.

    Friday, June 14, 2013 11:54 AM
  • If you want to use non-WinRT types in your methods, you should not make then WinRT components. Your store app is written in C++, and so it can make calls to regular C++ DLLs.

    You would only make a WinRT component when you want to make sure that it's WinRT compatible (meaning a C# store app would be able to call it fine).


    http://blog.voidnish.com

    Friday, June 14, 2013 12:11 PM
  • I am aware of that, but this still not seem to be normal behaviour and I would be interested in the reason of it.
    Friday, June 14, 2013 12:19 PM
  • Yes.  In the post that I deleted, the projects were built together.  I realized that this was probably not a true WinRT component.  My theory is that the behavior is due to reference counting cleanup.  That is, the component thinks the resource is no longer needed and is released.  Your non standard call to the component exposes this process which would not be apparent if you were passing legitimate arguments.

    It would be interesting to know if the value of the de-referenced pointer was correct inside the method.  That is what I was going for but I don't know how to reference (use) an external WinRT component in a C++ project.

    Friday, June 14, 2013 12:38 PM
  • If you build one of you projects as a WinRT component (the easiest is to use the WinRT component template), then you can add a reference in your Windows Store project to it (by simply right clicking on the project and selecting "References...").

    The pointer was still correct inside the method, I could properly draw the string to the screen with DirectX, but the first character of every word was missing, because the one wchar_t pointed to by the pointer (the first character) got erased.

    Friday, June 14, 2013 12:42 PM
  • I can't explain this behavior but I think a pointer parameter in a WinRT component public method is not an IN and OUT parametre but only an OUT parameter like in C#.

    http://msdn.microsoft.com/en-us/library/ee332485.aspx

    Friday, June 14, 2013 12:45 PM
  • Thanks.  I was able to duplicate your result.  

    Friday, June 14, 2013 12:49 PM
  • In string params should be const. you should also encode other parts of the specified behavior using SAL.

    _In_ _Nullterminated_ wchar_t const *

    PCWSTR is most of that, so

    _In_ PCWSTR

    COM rules say out params must be stabilized to zero. if there is a marshaling layer in between it might be implementing that for you.

    using Platform::String does not require a copy in thus case, a string reference avoids that.

    so use Platform::String and pass your wstring directly, the compiler will do the implicit conversion to HSTRING (Platform::String) via a string reference.

    Chris

    Sunday, June 16, 2013 5:30 PM
  • Thanks, I didn't know that, this is a definitive answer for why this is happening then. (Unfortunately I can not pass in a const wchar_t*, because that is not enabled for public methods in a WinRT component).

    You say that "a string reference avoids that". By this do you mean using the StringReference type? I don't think I can use that in this situation, because that is a native C++ type, which can not be used on the public interface of a WinRT component.

    If I simply create a Platfrom::String instance and pass in the wchar_t pointer and the length of my string, then a copy of that string is created in memory, right?

    Monday, June 17, 2013 8:11 AM