DLL export array reference to C#

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • ShadowLocke
    New Member
    • Jan 2008
    • 116

    DLL export array reference to C#

    I am trying to pass a string array from c# into c++ and manipulate the data of that array. How can I do this? (I am c++ newb)

    c++
    Code:
    __declspec(dllexport) int __stdcall Test(LPCTSTR* as_test)
    {
    	as_test[0] = L"This is new data";
                    return 0;
    }
    c#
    Code:
    [DllImport("PBNI.dll", CharSet = CharSet.Unicode)]
                static extern int Test(ref String[] as_ret);
    
           public void Testing()
           {
                    String[] ls = new String[] { "replace me", "more data", "even more" };
                    Test(ref ls);
                    Console.WriteLine(ls[0]);
           }
    I tried this, but I get two japanese chars..
  • weaknessforcats
    Recognized Expert Expert
    • Mar 2007
    • 9214

    #2
    Code:
     as_test[0] = L"This is new data";
    as_test is an LPCTSTR* but L"This is new data" is not a Unicode string.

    You might try:

    Code:
     as_test[0] = TEXT("This is new data");

    Comment

    • ShadowLocke
      New Member
      • Jan 2008
      • 116

      #3
      Thanks for the post, just gave it a try but still getting the same results :(

      Comment

      • weaknessforcats
        Recognized Expert Expert
        • Mar 2007
        • 9214

        #4
        Are you sure a String array is the same as an LPCTSTR?

        I suspect interoperabilit y rules are causing this.

        I did a Google advanced search using "interoperabili ty msdn String" and got a lot of hits. One of them said:

        By default, .NET strings are marshalled by COM Interop to LPTSTR in C++ and, vice versa, and so you have to explicitly marshal any other type of unmanaged string (including BSTR) to and from a .NET string using the MarshalAs attribute.

        There's no particular problem in inheriting from IUnknown or IDispatch but, if you inherit from the latter, you have to mark the C# interface declaration with the InterfaceType attribute.
        I am leaving it to you to research this.

        Comment

        • ShadowLocke
          New Member
          • Jan 2008
          • 116

          #5
          I've gotten a step closer by changing the c++ code to this..

          Code:
          __declspec(dllexport) int __stdcall Test(LPCWSTR * &as_test)
          { 
          	_tcscpy((wchar_t*)as_test[0], L"new data");
          
          	return 0;
          }
          But this replaces the string array with an array of the 1 element that was changed..I dont know why im losing the rest of the data

          Comment

          • weaknessforcats
            Recognized Expert Expert
            • Mar 2007
            • 9214

            #6
            _tcscpy is a TCHAR macro that resolves to either strcpy(ASCII) or wcscpy(Unicode) . That means you need an LPCTSTR argument.

            Then L"new data" does not produce the correct string. Use the TEXT or _T macro. This will call MultibyteToWide Char on the Unicode side.
            Code:
            __declspec(dllexport) int __stdcall Test(LPCTSTR as_test) 
            {  
                _tcscpy(as_test, TEXT("new data"); 
              
                return 0; 
            }
            I know you are passing in an array of C# strings but I suggest you pass in onyl one string until you get a result.

            Next, _tcscpy does not check that you have enough memory to make a copy. Plus the address is the address of the C# string. That causes _tcscpy to overwrite the C# string and you have no idea what the format of a C# string object is. Therefore, I expect corruption here.

            I don't see where you are marshaling the code on the C++ side and I don't see you using IDispatch on the C++ side. That is, COM does your marshaling aand it does it through IDispatch.

            You can't simply copy from one language to the other.

            Comment

            • ShadowLocke
              New Member
              • Jan 2008
              • 116

              #7
              Originally posted by weaknessforcats
              ... I know you are passing in an array of C# strings but I suggest you pass in onyl one string until you get a result. ...
              Originally posted by ShadowLocke
              ...
              But this replaces the string array with an array of the 1 element that was changed..I dont know why im losing the rest of the data
              Taking the array element out of the picture everything works as expected. I'm getting results, just not what im asking for. I need to return an edited array parameter..

              Passing the array of strings, in the c++ code I am able to messagebox(0, as_test[any index], L"", 0) and see that each string is indeed there. So the pointer in is working.

              I think i will end up turning the array into a delemited string but that just seems wrong..

              Originally posted by weaknessforcats
              ...
              Next, _tcscpy does not check that you have enough memory to make a copy. Plus the address is the address of the C# string. That causes _tcscpy to overwrite the C# string and you have no idea what the format of a C# string object is. Therefore, I expect corruption here. ...
              This is a test. I want to see results before I make it error proof.

              Originally posted by weaknessforcats
              ...
              I don't see where you are marshaling the code on the C++ side and I don't see you using IDispatch on the C++ side. That is, COM does your marshaling aand it does it through IDispatch. ...
              I know nothing of this IDispatch, please elaborate if you still think your on the right track. And since I see the data coming in properly, I think my problem here is arrays. Not strings.

              ...
              You can't simply copy from one language to the other.
              Hence...the orginal cry for help.

              Comment

              • weaknessforcats
                Recognized Expert Expert
                • Mar 2007
                • 9214

                #8
                I assume you have researched stuff like this:

                Learn about interoperability between C# and unmanaged code, including platform invoke, C++ interop, exposing COM components to C#, and exposing C# to COM.


                Links to content about the C++/CLI and C++/CX generics features, types, and methods.


                Review issues about calling a DLL function that can seem confusing. The function calling process differs depending on if the return type is blittable.

                Comment

                • ShadowLocke
                  New Member
                  • Jan 2008
                  • 116

                  #9
                  Problem solved with this:

                  c++
                  Code:
                  __declspec(dllexport) int __stdcall Test(LPCTSTR ** as_test)
                  {
                  	_tcscpy((wchar_t*)as_test[0], TEXT("a result."));
                  
                  	return 3;
                  }
                  c#
                  Code:
                  [DllImportAttribute("PBNI.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
                              public static extern void Test([In, Out] String[] as_test);
                  Arrays require the [In, Out] attributes to come back.

                  Comment

                  • weaknessforcats
                    Recognized Expert Expert
                    • Mar 2007
                    • 9214

                    #10
                    Your solution fails if your code is compiled using ASCII.

                    Also, it assumes a pointer to a pointer is the same as a pointer. And it assumes there is enough allocated memory at that location to contain the source string. And it assumes a C# String has the same format as a C string considering you are overwriting the C# string.

                    You need a solution that does not rely on a cast and that works for both ASCII and Unicode.

                    Comment

                    • ShadowLocke
                      New Member
                      • Jan 2008
                      • 116

                      #11
                      Originally posted by weaknessforcats
                      Your solution fails if your code is compiled using ASCII.

                      Also, it assumes a pointer to a pointer is the same as a pointer. And it assumes there is enough allocated memory at that location to contain the source string. And it assumes a C# String has the same format as a C string considering you are overwriting the C# string.

                      You need a solution that does not rely on a cast and that works for both ASCII and Unicode.
                      One..the question was about passing arrays from c# to c++ and back..not memory checking and error proofing..that is something do be done after I figured out the problem at hand.. Two..reading the material that you gave (after digging further around in to find something that remotly addressed the issue at hand [http://msdn.microsoft.com/en-us/library/hk9wyw21.aspx]) makes the same assumption about strings (Im gonna go ahead and take MSDN's word on it).. Three..thanks for telling me what i need on my current very specific project where i know exactly what is going into the function that i create and what is not (i.e. This function will always receive unicode from me..so sence it will never be compiled in ascii..why would i do extra work to account for it??). I honestly did not know what my project was meant to do until you told me.....

                      Stop tying to answer questions that are not asked...it just confuses bystanders

                      Comment

                      Working...