2

I am new to C#/.net programming.

I am marshaling the following C# struct from WPF code to a C++ class in an unmanaged C++ dll.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
 public struct CallbackParams
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string displayName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string userName;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string sipIdentity;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string sipProxyAddress;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string password;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string sipurl;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string calleeURI;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 releaseCallId;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 answerCallId;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 answerCode;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 timeout;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 rate_percent;
    }

The C++ class looks like this

    typedef struct CallbackParams_s {
    char displayName[80];
    char userName[80];
    char sipIdentity[80];
    char sipProxyAddress[80];
    char password[80];
    char sipurl[80];
    char calleeURI[80];

    unsigned int releaseCallId;
    unsigned int answerCallId;
    unsigned int answerCode;
    unsigned int timeout;
    unsigned int rate_percent;
} CallbackParams;

In the C# code

    public CallbackParams cb;
    public int code;
    [DllImport("XXXDll.dll", CharSet = CharSet.Ansi, CallingConvention =  CallingConvention.Cdecl)]
    public static extern int DLLCBFunc1([MarshalAs(UnmanagedType.I4)] int someCode, ref CallbackParams cbParams);

    cb.displayName = "XYZ";
    ... ...
    ... ...
    ...Init other fields in struct ....
    ...
    cb.calleeURI = "ABC";
    code = 0;
    DLLCBFunc1(code, ref cb);

In the above call the cb struct is marshalled correctly to the unmanaged dll. Now,

    cb.calleeURI = "DEF";
    code = 1;
    DLLCBFunc1(code, ref cb);

When the DLLCBFunc1 is called again, the code parameter is marshalled correctly in the unmanaged dll but cb.calleeURI is still set to the earlier "ABC" than "DEF".

What am I missing?

Appreciate your help.

EDIT:
Edited to provide more code
C++ code 
Class MyClass {
...
...
public:
   void SetCBParams(CallbackParams *cb) { cbParams = cb };
private:
   CallbackParams *cbParams;
}

MyClass *m_class;
extern "C" __declspec(dllexport) int DLLCBFunc1(int code, CallbackParams *cb) 
{
    if(code == 0) {
        m_class = new MyClass;
    }
    m_class->setCBParams(cb);
    ....
    ..call some func ...
}
John Qualis
  • 1,663
  • 4
  • 33
  • 52
  • This seems hard to believe. What does the C++ code look like? Also, that struct should not have Pack=1 I think, not that it matters here but you may as well get it logically correct. – David Heffernan Jul 05 '13 at 05:26
  • I've attempted to reproduce this behaviour and cannot. I believe that the behaviour that you describe does not actually happen. I suggest that you provide an SSCCE, otherwise this is not a question. – David Heffernan Jul 05 '13 at 11:51
  • I have provided more source code above. Its strange since I see this consistently. To get the right behaviour I had to declare another parameter cb1 (public CallbackParams cb1); cb1 = cb; cb1.userName="DEF"; DLLCBFunc1(code, ref cb1); – John Qualis Jul 05 '13 at 13:16
  • Nope, I cannot reproduce like that. To be honest, you are making a mess of this question. If you see it consistently, then make an SSCCE. You should be able to get it down to less than 30 lines of code in total. You cannot expect anyone to help when you have a non-reproducible problem that is specified in such a vague way. Provide us with a repro and for sure we will be able to explain it. – David Heffernan Jul 05 '13 at 13:20
  • I am using .net 4.5 and VS 2012. Feel free to edit it to make SSCCE. I am trying to be as short and clear as I can. The only other thing that I can add is that "MyClass" definition in the header file in my program has 200 lines of code and is full of C++11 functors and lambdas. – John Qualis Jul 05 '13 at 13:37
  • *Feel free to edit it to make SSCCE.* No, I'll feel free to let you do the work to make this into a real question. Do remember that I cannot reproduce what you say. So what if the header file has 200 lines of code? That's the essence of making an SSCCE - the fact that you strip out that which is not needed to illustrate the problem. If you won't take the time to make an SSCCE, why do you expect others to spend time on this. Make us care by showing that you care. – David Heffernan Jul 05 '13 at 13:39
  • Its not laziness but I am unsure that its the right way. Let me make try to reproduce it myself here in a test example. May be the presence of functors and lambdas in the class or may be something else which is causing it. And the problem is real for sure. I don't intend to waste my time or anyone else's if it wasn't. – John Qualis Jul 05 '13 at 13:49
  • I'm sure the problem is real. But since it can't readily be reproduced (well, I failed), then you need an SSCCE. – David Heffernan Jul 05 '13 at 13:50

1 Answers1

-1

answer:

Have a look at David Heffernan's comments - he seems more knowledgeable and to have spent a lot more time on this.

previous attempts:

Then again, after some more search, that should answer your exact question.

If you want to marshal to char* (which basically is the same as char[]) you will need to use a StringBuilder.

See this completely unrelated example that shows the signature from pinvoke.net.

You should then either recreate your structure or internally copy/convert it (if used elsewhere as well) to the following:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
 public struct CallbackParams
 {
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
     public StringBuilder displayName;

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
     public StringBuilder userName;
} 

Also be careful that StringBuilder already does all the marshalling automatically.. also there is no need for the ref anymore. Actually, using ref will cause problems.

cb.displayName = "XYZ";
cb.userName = "ABC";
code = 0;
DLLCBFunc1(code, cb);

Don't know out of my head about assigning strings to stringbuilders.. might be you have to do

cb.displayName = new StringBuilder("XYZ");

edit

As David Hefferman has pointed out.. I have missed the point. :)

So... in your case you probably will have to declare the char[80] as Byte[80] arrays and manually copying the string's content, also making sure to mind the likely unicode->ansi conversion. Also, after the call, you will need to copy back.

You can find some info about doing that here on StackOverflow.

edit

Put answer to top.

Community
  • 1
  • 1
Andreas Reiff
  • 7,961
  • 10
  • 50
  • 104
  • John doesn't want to marshal to char*. What gave you that impression? And the rest of your answer makes no sense at all. – David Heffernan Jul 05 '13 at 05:24
  • Correct me if I am wrong.. char* and char[] are the same in this context. – Andreas Reiff Jul 05 '13 at 05:27
  • Show me anywhere in the question where we have char* or char[]. The struct members are inline char arrays with length 80. No pointers. Your answer is plain nonsense. Sorry. – David Heffernan Jul 05 '13 at 05:30
  • Thanks for pointing that out so bluntly. :) But you are correct, I am afraid. I will edit answer. – Andreas Reiff Jul 05 '13 at 06:12
  • *So... in your case you probably will have to declare the char[80] as Byte[80] arrays and manually copying the string's content.* Absolutely not. Using `[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]` is perfect. I'd give up on this answer and start again...... – David Heffernan Jul 05 '13 at 07:10
  • I will read more carefully next time. Well, thx for pointing this out! – Andreas Reiff Jul 05 '13 at 11:41
  • It's still not an answer, because [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] is fine. – David Heffernan Jul 05 '13 at 11:44