Solved

Importing a C-style .dll AND marshalling confusing data types.

Posted on 2004-03-25
2
1,736 Views
Last Modified: 2012-06-27
I am using the .NET Interop services to import a .dll written in C.  One of the functions in the .dll has some very confusing data types.  I'm new to marshalling data types - anyone want to help?

//// The original C-style function declaration looks something like this:
__int32 FunctionName( void * A, const char * B, unsigned char * C, unsigned __int32 D, unsigned char ** E, unsinged __int32 * F, unsigned char ** G, unsigned __int32 * G);

//// My C# code will look something like this:
using System;
using System.Runtime.InteropServices;

namespace SomeNamespace
{
   public class SomeClass
   {
        [ DllImport( "SomeLibrary.dll" )]
        public static extern int FunctionName( ..... WHAT SHOULD I DO NEXT ????? )
   }
}

//////////
So, my question is: what C# treatment do I give all those ugly C-style data types???

Thanks!!!
0
Comment
Question by:TheGooch
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
2 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 10684760
All pointers (including **) should be passed as IntPtr.
Rwad about Marshal class. You need its functions to prepare input parameters to this function and to read output parameters.
For example, if A parameter expects pointer to some data, allocate memory block in C# using Marshal.AllocHGlobal Method. It returns IntPtr. Fill this block using unsafe programming and pass IntPtr to FunctionName.
If B points to ansi string, use Marshal.StringToHGlobalAnsi Method to allocate this string on non-managed heap and pass result IntPtr to the function.
Double pointer (**) may be passed as IntPtr ref or out parameter.
0
 
LVL 48

Accepted Solution

by:
AlexFM earned 500 total points
ID: 10685572
After reading your question I decided to make small C# PInvoke exersize. I hope it can help you. Consider the following C Dll:

// Function gets void* pointer and increments
// 3 starting bytes in it
SERVERDLL_API void Function1(void* p)
{
    char* pc = (char*) p;

    (*pc++)++;
    (*pc++)++;
    (*pc++)++;
}

// Function gets const pointer to string
SERVERDLL_API void Function2(const char* p)
{
    MessageBox(NULL, p, NULL, MB_OK);
}


// Function gets pointer to integer
SERVERDLL_API void Function3(int* p)
{
    *p = 10;
}

// Function gets char** and allocates ansi string
SERVERDLL_API void Function4(char** p)
{
    *p = new char[20];
    strcpy(*p, "string from C");
}

// Function releases char* string
SERVERDLL_API void Release(char* p)
{
    delete[] p;
}

This is C# console application which calls all these functions:


namespace PInvokeClient
{
    class Class1
    {

        #region void* parameter (Test 1)

        [DllImport ("ServerDll.dll")]
        public static extern void Function1(IntPtr p);

        static void Test1()
        {
            // allocate memory on unmanaged heap
            IntPtr ptr = Marshal.AllocHGlobal(3);

            // Fill is with 3 bytes
            unsafe
            {
                byte* pByte = (byte*)ptr;

                *pByte = 50;
                *(pByte+1) = 51;
                *(pByte+2) = 52;
            }

            Function1(ptr);   // increment each bytes

            // read result (output is 51, 52, 53)
            unsafe
            {
                byte* pByte = (byte*)ptr;

                Console.WriteLine( *pByte );
                Console.WriteLine( *(pByte+1) );
                Console.WriteLine( *(pByte+2) );
            }

            // free memory
            Marshal.FreeHGlobal(ptr);
        }
        #endregion


        #region const char* parameter (Test 2)

        [DllImport ("ServerDll.dll")]
        public static extern void Function2(IntPtr p);

        static void Test2()
        {
            String s = @"String from C#";

            // allocate string on unmanaged heap
            IntPtr ptr = Marshal.StringToHGlobalAnsi(s);

            Function2(ptr);


            // free memory
            Marshal.FreeCoTaskMem(ptr);
        }
        #endregion


        #region int* parameter (Test 3)

        [DllImport ("ServerDll.dll")]
        public static extern void Function3(IntPtr p);

        static void Test3()
        {
            // allocate memory on unmanaged heap
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));

            Function3(ptr);

            // read result
            unsafe
            {
                int* pInt = (int*)ptr;

                Console.WriteLine( *pInt );
            }

            // free memory
            Marshal.FreeHGlobal(ptr);
        }
        #endregion

        #region char** parameter (Test 4)

        [DllImport ("ServerDll.dll")]
        public static extern void Function4(out IntPtr p);
        [DllImport ("ServerDll.dll")]
        public static extern void Release(IntPtr p);

        static void Test4()
        {
            IntPtr ptr;

            Function4(out ptr);

            string s = Marshal.PtrToStringAnsi(ptr);

            Console.WriteLine(s);

            Release(ptr);

        }
        #endregion


        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Test1();
            Test2();
            Test3();
            Test4();
        }
    }
}

As you see, handling of C function parameters depends on their type and function algorithm. Marshal class and unsafe programming make C# program flexible enough to call various C functions.

Read also this article:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp09192002.asp
0

Featured Post

Online Training Solution

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action. Forget about retraining and skyrocket knowledge retention rates.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Article by: Najam
Having new technologies does not mean they will completely replace old components.  Recently I had to create WCF that will be called by VB6 component.  Here I will describe what steps one should follow while doing so, please feel free to post any qu…
Introduction This article series is supposed to shed some light on the use of IDisposable and objects that inherit from it. In essence, a more apt title for this article would be: using (IDisposable) {}. I’m just not sure how many people would ge…
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…

630 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question