Link to home
Start Free TrialLog in
Avatar of deming
deming

asked on

Calling C++ DLL from C#

I have a WORKING unmanaged DLL written in C++ that I am trying to call from C#.  Here are the declarations from C++:
__declspec(dllimport) BOOL __stdcall SaveStrData(const char *object, int index, const char *value);
__declspec(dllexport) BOOL __stdcall LoadStrData(const char *object, int index, char *retval);
__declspec(dllimport) char *__stdcall LoadStrData2(const char *object, int index);

Open in new window


Here is what I tried in C# in my Class1:
        [DllImport("IECGlobalVars32.dll", CharSet = CharSet.Ansi)]
        public static extern bool SaveStrData(string object1, int index, string sValue);

        [DllImport("IECGlobalVars32.dll", CharSet = CharSet.Ansi)]
        public static extern bool LoadStrData(string object1, int index, string sValue);

        [DllImport("IECGlobalVars32.dll", CharSet = CharSet.Ansi)]
        public static extern string LoadStrData2(string object1, int index);

Open in new window


The LoadStrData and LoadStrData2 both fail.

Here are my calls in C#:
        private void btnSaveStr_Click(object sender, EventArgs e)
        {
            String myStr;
            Int32 myIndex = 0;
            String myObj;
            myObj = tbObj.Text;
            myStr = tbValue.Text;
            myIndex = System.Convert.ToInt32(tbIndex.Text);
            Class1.SaveStrData(myObj, myIndex, myStr);
        }

        private void btnLoadStrData_Click(object sender, EventArgs e)
        {
            string myStr;
            Int32 myIndex = 0;
            String myObj;
            myObj = tbObj.Text;
            myStr = "nothing here";
            myIndex = System.Convert.ToInt32(tbIndex.Text);
            Class1.LoadStrData(myObj, myIndex, myStr);
            tbValue.Text = myStr;

        }

        private void btnLoadStrData2_Click(object sender, EventArgs e)
        {
            String myStr;
            Int32 myIndex = 0;
            String myObj;
            myObj = tbObj.Text;
            myStr = "nothing here";
            myIndex = System.Convert.ToInt32(tbIndex.Text);
            myStr = Class1.LoadStrData2(myObj, myIndex);
            tbValue.Text = myStr;

        }

Open in new window


QUESTION: What changes are necessary to make this work from C#?
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

C# strings are UNICODE (double byte), not single byte (as you have in the dll).  
Have a think which side will be simplest for you to make the changes to - the C# or the C++ side.
Avatar of deming
deming

ASKER

I can't make changes to the C++ side so they will have to be made on the C# side. Can you provide the code changes necessary?
You can try with stringbuilder
http://stackoverflow.com/questions/6153466/calling-a-c-dll-function-with-char-parameters-with-p-invoke
or byte array
 [DllImport("IECGlobalVars32.dll", CharSet = CharSet.Ansi)]
 public static extern bool SaveStrData(byte[] object1, int index, byte[] sValue);

[DllImport("IECGlobalVars32.dll", CharSet = CharSet.Ansi)]
        public static extern int ENCRYPT([MarshalAs(UnmanagedType.LPArray)]byte[] object1, int index, [MarshalAs(UnmanagedType.LPArray)]byte[] sValue);
when call function

private void btnSaveStr_Click(object sender, EventArgs e)
        {
            String myStr;
            Int32 myIndex = 0;
            String myObj;
            myObj = tbObj.Text;
            myStr = tbValue.Text;
            myIndex = System.Convert.ToInt32(tbIndex.Text);
            byte[] myObjByte= System.Text.Encoding.ASCII.GetBytes (myObj );
            byte[] myStrByte= System.Text.Encoding.ASCII.GetBytes (myStr );
            /// note: Can you need to allocate the more memory for myObjByte if necessary
            Class1.SaveStrData(myObjByte, myIndex, myStrByte);
        }
ASKER CERTIFIED SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of deming

ASKER

Great job Andy.  That worked for "LoadStrData" but not for "LoadStrData2".  Any other ideas for "LoadStrData2" ?
Change the return value of LoadStrData2 from String to IntPtr. Use Marshal.Copy when you need to copy data off the pointer. Ask the author of the dll if it could return null, and if so when. Also ask the author how much data can be safely copied starting from the returning address.

Ask the author of the dll if you need to free the memory pointed to by the returning char*, and if so, how. If the pointer is allocated on the C++ heap, there is no way for you to free the memory, and the author must provide a function that does a delete on the same heap. If the address is allocated on the COM heap via CoTaskMemAlloc you can Marshal.FreeCoTaskMem to free the memory.
Avatar of deming

ASKER

It turns out the LoadStrData2 is for VB6 using BSTR so it is not going to work with C#. Thank you everyone for your input and expertise.