views:

539

answers:

3

I have some C# code calling into an unmanaged C++ DLL. The method I am calling is intended to accept a string as a ref. To handle this I pass in a StringBuilder, otherwise there is a StackOverflowException.

This is working fine, but on some calls the string that comes back from the unmanaged code is a jumbled string like this: øŸE˜.,Ê. I know this must have something to do with encoding, but I've tried several things, listed below, and nothing works. This is not an issue in VB.Net code that someone else has written to do something similar.

Here's what I've tried: 1. I'm using this: [DllImport("dmphnx32.dll")], but have tried all Charset options without success.

  1. Tried to use Encoding.Default.GetBytes, Encoding.ASCII, Encoding.Unicode, and the rest without success.

I don't have any experience with C++ so I can really use the help.

Here's the DLLIMport method:

[DllImport("dmphnx32.dll")]
        public static extern int PhxQueryDataAttributes(int handle,
                                        StringBuilder lTableName,
                                        StringBuilder lColumnName,
                                        ref short lIteration,
                                        ref short type,
                                        ref short maxLen,
                                        ref short endorsement,
                                        StringBuilder endorsementId);

Here's the C++ code:

short DMEXP PhxQueryDataAttributes(HWND handle,
                                   char *lTableName,
                                   char *lColumnName,
                                   short *lIteration,
                                   short *Type,
                                   short *MaxLen,
                                   short *Endorsement,
                                   char  *EndorsementID)
{

    handle = PhxInfo.HiddenHwnd;
    strcpy(lTableName, PhxInfo.TableName);
    strcpy(lColumnName, PhxInfo.ColumnName);


    *Type = PhxInfo.PhnxDataType;
    // max len
    *MaxLen = PhxInfo.MaxDataLen;
    *Endorsement = PhxInfo.Endorsement;
    strcpy(EndorsementID, PhxInfo.EndorsementID);
    // determine which table we need the iteration of
    *lIteration = PhxIterationArray[PhxInfo.sEffectiveTableID];

    return SUCCESS;
}

Here's the C# code that calls into the unmanaged code:

public int PhxQueryDataAttributes(int handle, ref string lTableName, ref string lColumnName, 
            ref short lIteration, ref short type, ref short maxLen, ref short endorsement, 
            ref string endorsementId)
        {
            var sbTableName = new StringBuilder();
            var sbColName = new StringBuilder();
            var sbEndId = new StringBuilder();

            var ret = RatingProxy.PhxQueryDataAttributes(handle, sbTableName, sbColName,
            ref lIteration, ref type, ref maxLen, ref endorsement, sbEndId);


            lTableName = sbTableName.ToString();
            lColumnName = sbColName.ToString();
            endorsementId = sbEndId.ToString();
return ret;
}

Thanks, Corey

A: 

Instead of the StringBuilder, you might try using an IntPtr and the Marshal class.

SO, it would look like this:

[DllImport("dmphnx32.dll")]
    public static extern int PhxQueryDataAttributes(int handle,
                                    IntPtr lTableName,
                                    IntPtr  lColumnName,
                                    ref short lIteration,
                                    ref short type,
                                    ref short maxLen,
                                    ref short endorsement,
                                    IntPtr endorsementId);

Calling code would be:

public int PhxQueryDataAttributes(int handle, ref string lTableName, ref string lColumnName, 
        ref short lIteration, ref short type, ref short maxLen, ref short endorsement, 
        ref string endorsementId)
    {
        var sbTableName = Marshal.AllocHGlobal(1024);//Change these to the max length possible for each string.
        var sbColName = Marshal.AllocHGlobal(1024);
        var sbEndId = = Marshal.AllocHGlobal(1024);

        var ret = RatingProxy.PhxQueryDataAttributes(handle, sbTableName, sbColName,
        ref lIteration, ref type, ref maxLen, ref endorsement, sbEndId);


        lTableName = Marshal.PtrToStringAnsi(sbTableName);
        lColumnName = Marshal.PtrToStringAnsi(sbColName);
        endorsementId = Marshal.PtrToStringAnsi(sbEndId);
        return ret;
}

This way you can specify the correct encoding (which you cannot do with a string builder, which assumes it is a UTF type encoding). Experiment a little, and see which of the Marshal.PtrToString... ones work, though ANSI should work, since that is what most C++ libraries use.

Erich
I tried this method and experimented with the different types of encoding but I'm still getting my encoded string back. I wonder if something else is going on?
Corey Coogan
Did you notice that the C++ version uses 'handle' as a HWND, and you are using it as an int? HWND is a Int64 as far as I know (a long).The only other thing I can think of is that perhaps you have an alignment issue.
Erich
+1  A: 

Ok, I see your problem. You need to allocate a capacity for the StringBuilder, you cant just use the default.

leppie
This looked very promising it its simplicity, but unfortunately it didn't work. I still get the same strangely encoded value.
Corey Coogan
A: 

After I tried the first 2 answers and learned that they weren't helping, I knew that something else must be suspect. I found a small bug somewhere else in my app where I was actually missing an initialization param on the unmanaged code. This was causing my strangely encoded string.

Thanks for the help, Corey

Corey Coogan