none
Calling C++ metro dll from c# metro app and returning filled structures

    Question

  • Hi,

    I have created standard C++ Metro DLL and C# Metro App, Metro app calls functions from C++ metro DLL, but when I call to one of exported function of C++ metro dll which returns a structure as out parameter, this structure contains wrong values.

    Following function exported from C++ metro dll:

    BOOLEAN __stdcall GetMessageInformation(EXAMPLE_STRUCT *pExampleStruct);

    Where,

    #pragma pack(push, 1)
    typedef struct tagEXAMPLE_STRUCT
    {
    int        iinfo;
    int        iExtrainfo;
    BOOLEAN       bInternet;
    BOOLEAN       bMessage;
    DWORD       dwMessageCount;
    WCHAR       wszVersion[30];

    } EXAMPLE_STRUCT, *P_EXAMPLE_STRUCT;
    #pragma pack(pop)

    I have convereted the structure in C# app with Pragma pack 1, so that I can call GetMessageInformation.

    From my C++ metro DLL, I am returning hardcoded values for all structure members. still I am getting different values. It seems to be something related to structure  alignment. please help me to resolve this.

    One observation: if we programatically calculate the size of structure in C++ and C# separately, it gives different results.

    Thanks,

    Vikas Tiwari.

    Monday, July 30, 2012 9:42 AM

All replies

  • I'd look into how you have defined the structure in C# to make sure it actually is the same as the structure in the C++ side. That is the most common source of p-invoke errors that I've seen. In particular, watch out for size of the the BOOLEAN definitions.

    If you can confirm that but still have a problem we would need minimal repro code for both sides of the problem to give more detailed analysis.

    --Rob

    Tuesday, July 31, 2012 12:47 AM
  • Hi Rob Caplan,

    Thanks for your reply.
    Here I am attaching code for C++ and C# app.

    C++ Code:

    //
    //	Structure declaration in .h
    //
    #pragma pack(push, 1)
    typedef struct tagMESSAGE_INFO
    {
    	int	iInfo;
    	int	iExtraInfo;
    	BOOLEAN	bInternet;
    	BOOLEAN	bMessage;
    	DWORD	dwMessageCount;
    	WCHAR	wszVersion[30];
    
    }	MESSAGE_INFO, *P_MESSAGE_INFO;
    #pragma pack(pop)
    
    //
    //	Exported function declaration in .h
    //	We have used .def file to export.
    //
    BOOLEAN __stdcall
    GetMessageInfo(
    	MESSAGE_INFO *pMessageInfo
    	);
    
    //
    //	Definition of exported function in .cpp
    //
    BOOLEAN __stdcall
    GetMessageInfo(
    	MESSAGE_INFO *pMessageInfo
    	)
    {
    	MESSAGE_INFO MessageInfo
    	if (NULL == pMessageInfo)
    	{
    		return FALSE;
    	}
    
    	memsetMessageInfo, 0, sizeof(MESSAGE_INFO));
    	MessageInfo.iInfo = 2;
    	MessageInfo.iExtraInfo = 1;
    	MessageInfo.bInternet = false;
    	MessageInfo.bMessage = true;
    	MessageInfo.dwMessageCount = 2;
    	wcscpy(MessageInfo.wszVersion, L"14.00(7.0.0.1)");
    
    	*pMessageInfo = MessageInfo;
    
    	return TRUE;
    }
    


    C# Code:

    //
    //	Structure declared in a class of .cs
    //
    [StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Unicode)]
    public struct MESSAGE_INFO
    {
    	public Int32	iInfo;
    	public Int32	iExtraInfo;
    	public byte	bInternet;
    	public byte	bMessage;
    	public UInt32	dwMessageCount;
    	[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 30)]
    	public string	wszVersion;
    }
    
    //
    //	Dll location
    //
    const string strDLLLocation = "test.dll";
    
    //
    //	Declaration of exported function from dll
    //
    [DllImport(strDLLLocation, CallingConvention = CallingConvention.StdCall)]
    public static extern bool GetMessageInfo(out MESSAGE_INFO pMessageInfo);
    
    //
    //	Calling function from
    //
    private void m_Btn_Read_Click(object sender, RoutedEventArgs e)
    {
    	Boolean bRet;
    
    	MESSAGE_INFO MessageInfo;
    	bRet = GetMessageInfo(out MessageInfo;);
    	if (false == bRet)
    	{
    		return;
    	}
    
    	//
    	// Convert all fields from structure into string.
    	//
    	String strResult = "";
    
    	strResult += "iInfo=" + MessageInfo.iInfo.ToString();
    	strResult += "\n";
    	strResult += "iExtraInfo=" + MessageInfo.iExtraInfo.ToString();
    	strResult += "\n";
    	strResult += "bInternet=" + MessageInfo.bInternet.ToString();
    	strResult += "\n";
    	strResult += "bMessage=" + MessageInfo.bMessage.ToString();
    	strResult += "\n";
    	strResult += "dwMessageCount=" + MessageInfo.dwMessageCount.ToString();
    	strResult += "\n";
    	strResult += "Version=" + MessageInfo.wszVersion.ToString();
    	strResult += "\n";
    
    	//
    	// Set string to TextBlock on UI.
    	//
    	stcResult2.Text = strResult;
    }

    In above case, if we check sizes of both structures programmatically, it gives different result.
    Therefore we get different result in structure as out parameter after returning from function.

    Thanks,
    Vikas Tiwari.

    Wednesday, August 08, 2012 7:35 AM
  • You don't give enough information to know for sure: are you building x86 or x64? What sizes do you actually calculate? Etc. It is essential to know what exactly you are doing, what results you get, and how does that differ from the results that you actually get.

    That said, different results look reasonable. You use different sized strings in the C++ (WCHAR - 2 bytes) and C# (TCHAR - 1 byte for x86, 2 for x64) versions. This doesn't have anything to do with packing.

    A good way to analyze signature problems is to load the struct with known data in each field, marshall it across, and compare what the received bytes are to see where the original known data landed.

    --Rob

    Wednesday, August 08, 2012 7:51 AM