Click here to Skip to main content
15,881,856 members
Please Sign up or sign in to vote.
4.10/5 (3 votes)
See more:
I wrote some testcase named LibTest.dll (act like strcpy) in VC++ 6.0

LibTest.h shown as below:
///////////////////////////////////////////////////////////////////////////////
#define DLLEXPORT extern "C" __declspec(dllexport)

DLLEXPORT void LibTestInit();
DLLEXPORT void LibTestStrCpy(char *in, char *out);
DLLEXPORT void LibTestCleanup();

LibTest.cpp shown as below:
///////////////////////////////////////////////////////////////////////////////
#include "LibTest.h"

#include <stdio.h>

namespace CCTEC 
{
    class Test 
    {
    public:
        Test() {}
        ~Test() {}

    public:
        void strcpy(char *in, char *out) 
        {
            while (*out++ = *in++);
        }
    };
};

static CCTEC::Test *m_Test = NULL;

DLLEXPORT void LibTestInit() 
{
    m_Test = new CCTEC::Test(); 
}

DLLEXPORT void LibTestCleanup() 
{
    if (m_Test) delete m_Test; m_Test = NULL; 
}

DLLEXPORT void LibTestStrCpy(char *in, char *out) 
{
    if (m_Test) m_Test->strcpy(in, out);
}</stdio.h>


Then using DllImport to call unmanaged functions (such as LibTestInit )
that are implemented in a DLL (LibTest.dll)

LibTest.cs (part of the source code) shown as below:

using System.Runtime.InteropServices;

public class LibTest
{
    #region DLL Import
    [DllImport("LibTest")]
    private static extern void LibTestInit();

    [DllImport("LibTest")]
    private static extern void LibTestCleanup();

    [DllImport("LibTest")]
    private static extern void LibTestStrCpy(byte[] inStr, byte[] outStr);
    #endregion

    public LibTest() 
    {
        LibTestInit();
    }

    public void Dispose() 
    {
        LibTestCleanup();
    }

    public void StrCpy(byte[] inStr, byte[] outStr) 
    {
        LibTestStrCpy(inStr, outStr);
    }
}


And Form1.cs (with a TextBox control to test the output) shown as below:

m_LibTest1 = new LibTest();
byte[] outStr1 = new byte[1024];
m_LibTest1.StrCpy(System.Text.Encoding.Default.GetBytes("Hello World"), outStr1);
m_LibTest1.Dispose();
m_LibTest1 = null;
textBox.Text += System.Text.Encoding.Default.GetString(outStr1);
MessageBox.Show(System.Text.Encoding.Default.GetString(outStr1));

m_LibTest2 = new LibTest();
byte[] outStr2 = new byte[1024];
m_LibTest2.StrCpy(System.Text.Encoding.Default.GetBytes("Hello Linux"), outStr2);
m_LibTest2.Dispose();
m_LibTest2 = null;
// FIXME: There is no Hello Linux shown in the textBox.Text
textBox.Text += System.Text.Encoding.Default.GetString(outStr2);
// XXX: But shown by MessageBox
MessageBox.Show(System.Text.Encoding.Default.GetString(outStr2));


Expected output: Hello WorldHello Linux
Actual output: Hello World

So Who Moved My "Hello Linux" byte[] ?
Please someone catched the thief who moved my byte[], thanks a lot!
Posted
Updated 31-Mar-11 19:56pm
v2
Comments
Wendelius 1-Apr-11 1:56am    
Pre tags added
phil.o 27-Mar-15 16:16pm    
Not me! I'm innocent, I swear.

You can use "MarshalAs" in your C# dllimport function.
I think it's a problem related to marshaling only.
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 1-Apr-11 2:29am    
No! You could adivice not "calling" but application of attribute MarshalAs, but only one method has parameters.

--SA
The problem looks trivial, but I would need some time to debug it. Right now, I see only one problem here: the type *char by default is marchalled as System.String. Seriosly! See standard marshalling.

—SA
 
Share this answer
 
Comments
Olivier Levrey 1-Apr-11 4:54am    
If OP wants to fill a string, the best option is StringBuilder. See my answer.
Sergey Alexandrovich Kryukov 1-Apr-11 5:07am    
Thank you. Yes, I understand. I was just a quick hint.
Let me see your answer...
--SA
From C# code:
- If you want to pass a string as input parameter, use string type.
- If you want to pass a string and modify its content, then use StringBuilder type.

C#
public class LibTest
{
    //and don't forget to tell how big is your buffer
    [DllImport("LibTest")]
    private static extern void LibTestStrCpy(
       //this in an input string. So pass it as a string
       string inStr,
       //this is an output string. So pass it as StringBuilder
       StringBuilder outStr,
       //this is the size of the output buffer (the capacity of the StringBuilder)
       int maxLength);

    public void Test()
    {
        string inputString = "testing...";
        //create a StringBuilder with enough storage
        StringBuilder str = new StringBuilder(100);
        //call the function
        LibTestStrCpy(inStr, str, str.Capacity);
        //gets the output string
        string outputString = str.ToString();
        ...
    }    
}


And modify your C++ code like that:
C++
class Test
{
...
    void strcpy(const char *in, char *out, int sizeOfOut)
    {
        for (int i = 0; i < sizeOfOut - 1 && *in != '\0'; i++)
            *out++ = *in++;
        *out = '\0';
    }
...
};

DLLEXPORT void LibTestStrCpy(const char *in, char *out, int sizeOfOut)
{
    if (m_Test)
        m_Test->strcpy(in, out, sizeOfOut);
}
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 1-Apr-11 5:09am    
Oh, I see: you modified original signature; very good. I'll vote 5.
--SA
Olivier Levrey 1-Apr-11 5:10am    
Thanks!
You're in real danger! Somebody is moving your bytes. However, it can be "what", not "who"… who knows how to call it? Changes are, SPC will need to capture it.

Please see: [WARNING!] Black Line of Death in windows phone 7.

Be extra careful,
—SA
 
Share this answer
 
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900