Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
CODE PROJECT For Those Who Code
  • Home
  • Articles
  • FAQ
Community
  1. Home
  2. General Programming
  3. C#
  4. Question about Interop with a DLL written in C++

Question about Interop with a DLL written in C++

Scheduled Pinned Locked Moved C#
performancecsharpc++databasecom
5 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    kmansari
    wrote on last edited by
    #1

    I have a DLL that was written using C++. I am required to write a C# program that calls into the DLL and gets some information out of it. The DLL entry points that I have to call are declared as: __declspec(dllexport) HostInfo* GetHostInfoFromDb (int iHostId); __declspec(dllexport) void FreeHostInfoStruct (void* pHostInfo); struct { char* pHostName; int iId; char* pOwner; ... ... } HostInfo, *P_HostInfo; HostInfo is a structure containing a bunch of flat fields (i.e, all basic data types except for a couple of char*'s ). When GetHostInfoFromDb is called, it allocates a HostInfo structure internally, populates it with the information from a database, and retuns me the pointer to it. The way I did this in C# is as follows: [DllImport @"hostinfo.dll",EntryPoint="GetHostInfoFromDb")] public static extern IntPtr GetHostInfoFromDb(int iHostId); [DllImport @"hostinfo.dll",EntryPoint="FreeHostInfoStruct")] public static extern void FreeHostInfoStruct(IntPtr pHostInfo); [StructLayout(LayoutKind.Sequential,Size=116,CharSet=CharSet.Ansi )] public struct ManagedHostInfo { string szHostName; int iId; string szOwner; ..... ..... } I could successfully get my DLL to return the information I was looking for by calling GetHostInfoFromDb. I marshal the returned IntPtr using Marshal.PtrToStructure. However, when I try to call FreeHostInfoStruct, the DLL doesn't seem to free up the memory it had allocated. After a few hundred calls, the memory kept growing and started impacting the system performance. I verified this by first printing out the address of the HostInfo struct that the DLL allocated during a call to GetHostInfoFromDb. Next, I print out the address of pHostInfo during my call to FreeHostInfoStruct. The two addresses are different. What was initially allocated is not being freed up during FreeHostInfoStruct. C# code C++ Code --------------------------------------------------------------------------------------- 1. IntPtr iPtr = GetHostInfoFromDb (0); ---> P_HostInfo pInfo = new HostInfo (); // Populate pInfo fields // using info from database return pInfo; //Say, pInfo = 0x46F53A44 2. ManagedHostInfo hostInfo = Marshal.PtrToStructure (iPtr); // If

    H 1 Reply Last reply
    0
    • K kmansari

      I have a DLL that was written using C++. I am required to write a C# program that calls into the DLL and gets some information out of it. The DLL entry points that I have to call are declared as: __declspec(dllexport) HostInfo* GetHostInfoFromDb (int iHostId); __declspec(dllexport) void FreeHostInfoStruct (void* pHostInfo); struct { char* pHostName; int iId; char* pOwner; ... ... } HostInfo, *P_HostInfo; HostInfo is a structure containing a bunch of flat fields (i.e, all basic data types except for a couple of char*'s ). When GetHostInfoFromDb is called, it allocates a HostInfo structure internally, populates it with the information from a database, and retuns me the pointer to it. The way I did this in C# is as follows: [DllImport @"hostinfo.dll",EntryPoint="GetHostInfoFromDb")] public static extern IntPtr GetHostInfoFromDb(int iHostId); [DllImport @"hostinfo.dll",EntryPoint="FreeHostInfoStruct")] public static extern void FreeHostInfoStruct(IntPtr pHostInfo); [StructLayout(LayoutKind.Sequential,Size=116,CharSet=CharSet.Ansi )] public struct ManagedHostInfo { string szHostName; int iId; string szOwner; ..... ..... } I could successfully get my DLL to return the information I was looking for by calling GetHostInfoFromDb. I marshal the returned IntPtr using Marshal.PtrToStructure. However, when I try to call FreeHostInfoStruct, the DLL doesn't seem to free up the memory it had allocated. After a few hundred calls, the memory kept growing and started impacting the system performance. I verified this by first printing out the address of the HostInfo struct that the DLL allocated during a call to GetHostInfoFromDb. Next, I print out the address of pHostInfo during my call to FreeHostInfoStruct. The two addresses are different. What was initially allocated is not being freed up during FreeHostInfoStruct. C# code C++ Code --------------------------------------------------------------------------------------- 1. IntPtr iPtr = GetHostInfoFromDb (0); ---> P_HostInfo pInfo = new HostInfo (); // Populate pInfo fields // using info from database return pInfo; //Say, pInfo = 0x46F53A44 2. ManagedHostInfo hostInfo = Marshal.PtrToStructure (iPtr); // If

      H Offline
      H Offline
      Heath Stewart
      wrote on last edited by
      #2

      First - a few words about your unmanaged signatures. You should almost never return void. For your "free" functions, this can be okay but sometimes you might gets errors back that would be good to know about. Mostly, never return pointers. Return error codes (like HRESULTs) and declare your previous return values as [out] parameters. If you did that, your unmanaged to managed mapping would be easy and you wouldn't have to worry about marshaling the struct yourself:

      // unmanaged
      __declspec(dllexport) HRESULT GetHostInfoFromDB(int iHostID, P_HostInfo hostInfo);
       
      // managed
      [DllImport(...)]
      extern static int GetHostInfoFromDB(int iHostID, out HostInfo hostInfo);

      I would also warn that if you want to support both 32- and 64-bit architectures, your native int is equivalent to an IntPtr (both platform-dependent bit widths). Moving on, though... You need to pin your structure in memory using the GCHandle class (see the documentation in the .NET Framework SDK). The GC can move this around so that when you pass the address back to FreeHostInfoStruct. You can also use Marshal.DestroyStructure, which you typically should use when you use Marshal.PtrToStructure. In this case, however - since your freeing memory in unmanaged code - pinning the object should solve the problem. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog]

      K 2 Replies Last reply
      0
      • H Heath Stewart

        First - a few words about your unmanaged signatures. You should almost never return void. For your "free" functions, this can be okay but sometimes you might gets errors back that would be good to know about. Mostly, never return pointers. Return error codes (like HRESULTs) and declare your previous return values as [out] parameters. If you did that, your unmanaged to managed mapping would be easy and you wouldn't have to worry about marshaling the struct yourself:

        // unmanaged
        __declspec(dllexport) HRESULT GetHostInfoFromDB(int iHostID, P_HostInfo hostInfo);
         
        // managed
        [DllImport(...)]
        extern static int GetHostInfoFromDB(int iHostID, out HostInfo hostInfo);

        I would also warn that if you want to support both 32- and 64-bit architectures, your native int is equivalent to an IntPtr (both platform-dependent bit widths). Moving on, though... You need to pin your structure in memory using the GCHandle class (see the documentation in the .NET Framework SDK). The GC can move this around so that when you pass the address back to FreeHostInfoStruct. You can also use Marshal.DestroyStructure, which you typically should use when you use Marshal.PtrToStructure. In this case, however - since your freeing memory in unmanaged code - pinning the object should solve the problem. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog]

        K Offline
        K Offline
        kmansari
        wrote on last edited by
        #3

        Heath, Thanks a lot for your post. I completlely agree with everything you said about my unmanaged signatures. Unfortunately, the DLL that I am trying to interface with is also being used by other groups in my company. Making it return HRESULT (and other accompanying changes) would be a substantial effort, since it is not just one API we are talking about here. About your suggestion to pin down the structure before I can pass its address to the free function: Is it really required since the memory was allocated in the unmanaged world? If my understanding is right (and most likely it is not), the GC doesn't care about unmanaged memory. So let's go through the example once again: 1. From my managed code, I call GetHostInfoFromDB (this is exported by my unmanaged HostInfo.DLL) 2. GetHostInfoFromDB allocates unmanaged memory, initializes it with the data from the DB, and returns me a pointer to the unmanaged block of memory. 3. In my managed world, what I get is an IntPtr. So I use PtrToStruct to be able to see my data. 4. After processing the data, I have to call the unmanaged code again - this time, I am requesting it to free up the memory that it allocated during the first call 5. It is now that I pass the IntPtr that I got back from GetHostInfoFromDB. However, it turns out that the value of IntPtr received by the unmanaged world is different that what was passed from the managed code. Now, going back to your suggestion about pinning down the memory: I wasn't sure which memory you wanted me to pin down, since we are not allocating any memory in the managed world. However, I took your suggestion and pinned down the IntPtr that I get back from GetHostInfoFromDB. IntPtr iPtr = GetHostInfoFromDb (0); // Get the pointer to host info GCHandle gcHandle = GCHandle.Alloc (iPtr, GCHandleType.Pinned); // Pin down the structure IntPtr hostInfoStruct = gcHandle.AddrOfPinnedObject(); // Get the addr. of the pinned struct /* --- Call PtrToStruct here, and consume the host information here *---/ FreeHostInfoStruct (hostInfoStruct); // free up the memory gcHandle.Free (); // finally, free up the GC handle I still see the same problem with those changes. Next, I tried using FreeHostInfoStruct ((IntPtr)gcHandle); // free up the memory instead of FreeHostInfoStruct (hostInfoStruct); // free up the memory But that didn't solve the problem either. Any more ideas? Thanks, -Kamran

        1 Reply Last reply
        0
        • H Heath Stewart

          First - a few words about your unmanaged signatures. You should almost never return void. For your "free" functions, this can be okay but sometimes you might gets errors back that would be good to know about. Mostly, never return pointers. Return error codes (like HRESULTs) and declare your previous return values as [out] parameters. If you did that, your unmanaged to managed mapping would be easy and you wouldn't have to worry about marshaling the struct yourself:

          // unmanaged
          __declspec(dllexport) HRESULT GetHostInfoFromDB(int iHostID, P_HostInfo hostInfo);
           
          // managed
          [DllImport(...)]
          extern static int GetHostInfoFromDB(int iHostID, out HostInfo hostInfo);

          I would also warn that if you want to support both 32- and 64-bit architectures, your native int is equivalent to an IntPtr (both platform-dependent bit widths). Moving on, though... You need to pin your structure in memory using the GCHandle class (see the documentation in the .NET Framework SDK). The GC can move this around so that when you pass the address back to FreeHostInfoStruct. You can also use Marshal.DestroyStructure, which you typically should use when you use Marshal.PtrToStructure. In this case, however - since your freeing memory in unmanaged code - pinning the object should solve the problem. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog]

          K Offline
          K Offline
          kmansari
          wrote on last edited by
          #4

          Heath, What finally did solve the problem was undoing a stupid copy-and-paste mistake which had just got lost in the jungle of declarations... and the mistake? My managed signature declared the FreeHostInfoStruct function as returning a structure by value (DUH!!!) I modified it to return a void (which is how the unmanaged declaration was), everything started to work just fine. I guess declaring it the wrong way might have caused the .NET CLR to construct a screwed up call stack. I still have the GCHandle question for you though. Am I really required to pin down memory that was allocated in the unmanaged world? Thanks a lot for your help. -Kamran

          H 1 Reply Last reply
          0
          • K kmansari

            Heath, What finally did solve the problem was undoing a stupid copy-and-paste mistake which had just got lost in the jungle of declarations... and the mistake? My managed signature declared the FreeHostInfoStruct function as returning a structure by value (DUH!!!) I modified it to return a void (which is how the unmanaged declaration was), everything started to work just fine. I guess declaring it the wrong way might have caused the .NET CLR to construct a screwed up call stack. I still have the GCHandle question for you though. Am I really required to pin down memory that was allocated in the unmanaged world? Thanks a lot for your help. -Kamran

            H Offline
            H Offline
            Heath Stewart
            wrote on last edited by
            #5

            kmansari wrote: I guess declaring it the wrong way might have caused the .NET CLR to construct a screwed up call stack. Keep in mind that the term unmanaged means that the CLR does not managed the memory. When you call into unmanaged code, the CLR has no way of tracking memory. Just FYI. :) kmansari wrote: I still have the GCHandle question for you though. Am I really required to pin down memory that was allocated in the unmanaged world? No, but when alloc'ing it in the managed world and passing it to unmanaged code you do. Depending on what calls you make, though - like Marshal.PtrToStructure - you may need to pin the object because once you marshal the struct the GC tracks it and may move it. Chances are that won't happen - especially with value types which are declared on the stack - but it just something to keep in mind for future development. This posting is provided "AS IS" with no warranties, and confers no rights. Software Design Engineer Developer Division Sustained Engineering Microsoft [My Articles] [My Blog]

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups