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
  1. Home
  2. General Programming
  3. .NET (Core and Framework)
  4. P/Invoke "why" question

P/Invoke "why" question

Scheduled Pinned Locked Moved .NET (Core and Framework)
questioncsharpjsontutorial
9 Posts 4 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.
  • G Offline
    G Offline
    Gordon Kushner
    wrote on last edited by
    #1

    Here is one for you Win32 experts. When I as a Framework developer need to use a Win API function, I can declare the function with parameters I need rather than with strict type checking. For example, check out the underlined code in SendMessage() [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); StringBuilder? Hello? What perplexes me is that the API is expecting a pointer but we pass in a .NET StringBuilder. Please explain to a semi-newbie what dark magic allows this to happen. Thank you. Gordon

    L D D 3 Replies Last reply
    0
    • G Gordon Kushner

      Here is one for you Win32 experts. When I as a Framework developer need to use a Win API function, I can declare the function with parameters I need rather than with strict type checking. For example, check out the underlined code in SendMessage() [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); StringBuilder? Hello? What perplexes me is that the API is expecting a pointer but we pass in a .NET StringBuilder. Please explain to a semi-newbie what dark magic allows this to happen. Thank you. Gordon

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      That one is pretty simple, at least on the surface: all the compiler does is check consistency between your calling code and the prototype(s) you provide (both in C# or VB.NET), as if the native method were a managed one (but with the implementation lacking). The compiler never looks at the DLL file at all, it does not even have to be present while building. At run-time what gets passed is not type-checked at all, so whether it is an array pointer, a StringBuilder reference, or an IntPtr, as long as it points to the actual data, it could work. FYI: there may be some data conversion going on behind the scene, e.g. when the data passed is a string and the native method uses ANSI (8-bit!), the "Marshaling" will convert to/from Unicode (16-bit) for you automaticaally (using a temporary buffer, so also faking the pointer value!). :)

      Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

      Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

      modified on Monday, August 9, 2010 3:05 PM

      G 1 Reply Last reply
      0
      • L Luc Pattyn

        That one is pretty simple, at least on the surface: all the compiler does is check consistency between your calling code and the prototype(s) you provide (both in C# or VB.NET), as if the native method were a managed one (but with the implementation lacking). The compiler never looks at the DLL file at all, it does not even have to be present while building. At run-time what gets passed is not type-checked at all, so whether it is an array pointer, a StringBuilder reference, or an IntPtr, as long as it points to the actual data, it could work. FYI: there may be some data conversion going on behind the scene, e.g. when the data passed is a string and the native method uses ANSI (8-bit!), the "Marshaling" will convert to/from Unicode (16-bit) for you automaticaally (using a temporary buffer, so also faking the pointer value!). :)

        Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

        Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

        modified on Monday, August 9, 2010 3:05 PM

        G Offline
        G Offline
        Gordon Kushner
        wrote on last edited by
        #3

        Thank you, Luc!

        L 1 Reply Last reply
        0
        • G Gordon Kushner

          Thank you, Luc!

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          you're welcome. :)

          Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

          Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

          1 Reply Last reply
          0
          • G Gordon Kushner

            Here is one for you Win32 experts. When I as a Framework developer need to use a Win API function, I can declare the function with parameters I need rather than with strict type checking. For example, check out the underlined code in SendMessage() [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); StringBuilder? Hello? What perplexes me is that the API is expecting a pointer but we pass in a .NET StringBuilder. Please explain to a semi-newbie what dark magic allows this to happen. Thank you. Gordon

            D Offline
            D Offline
            DaveyM69
            wrote on last edited by
            #5

            Luc's given you a brilliant answer! In my experience, anything that takes the same memory footprint can be used. This makes int/uint (4 bytes), or even long/Size (8 bytes) interchangable. In the case of pointers, any reference type (or value type if used with ref [or out depending on the situation]) will work as it's the pointer to the object that's passed not the value pointed to. Whether the value stored at the address can be understood on the native side or managed side once returned is another matter of course!

            Dave

            If this helped, please vote & accept answer!

            Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum.(Pete O'Hanlon)
            BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

            L G 2 Replies Last reply
            0
            • D DaveyM69

              Luc's given you a brilliant answer! In my experience, anything that takes the same memory footprint can be used. This makes int/uint (4 bytes), or even long/Size (8 bytes) interchangable. In the case of pointers, any reference type (or value type if used with ref [or out depending on the situation]) will work as it's the pointer to the object that's passed not the value pointed to. Whether the value stored at the address can be understood on the native side or managed side once returned is another matter of course!

              Dave

              If this helped, please vote & accept answer!

              Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum.(Pete O'Hanlon)
              BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

              L Offline
              L Offline
              Luc Pattyn
              wrote on last edited by
              #6

              :rose:

              Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

              Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

              1 Reply Last reply
              0
              • G Gordon Kushner

                Here is one for you Win32 experts. When I as a Framework developer need to use a Win API function, I can declare the function with parameters I need rather than with strict type checking. For example, check out the underlined code in SendMessage() [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, StringBuilder lParam); StringBuilder? Hello? What perplexes me is that the API is expecting a pointer but we pass in a .NET StringBuilder. Please explain to a semi-newbie what dark magic allows this to happen. Thank you. Gordon

                D Offline
                D Offline
                Daniel Grunwald
                wrote on last edited by
                #7

                The .NET P/Invoke marshaller has special knowledge about some types. For example when passing a String, it passes the pointer to the string data, not the pointer to the managed string object. When passing a StringBuilder, the .NET marshaller passes a pointer to the StringBuilder's internal buffer. Another useful idiom is the SafeHandle: you can create your own SafeHandle-derived class and directly use that in P/Invoke signatures. The .NET marshaller will pass the underlying handle (IntPtr) instead. This is extremely useful because it avoids a number of problems: for example the finalizer could run when there are no active references to your object, but the underlying handle is still being used in a P/Invoke call. Using a SafeHandle directly in the P/Invoke signature makes the GC aware that the handle is used during the lifetime of the P/Invoke call, and it'll avoid finalizing the object until the call is complete.

                G 1 Reply Last reply
                0
                • D Daniel Grunwald

                  The .NET P/Invoke marshaller has special knowledge about some types. For example when passing a String, it passes the pointer to the string data, not the pointer to the managed string object. When passing a StringBuilder, the .NET marshaller passes a pointer to the StringBuilder's internal buffer. Another useful idiom is the SafeHandle: you can create your own SafeHandle-derived class and directly use that in P/Invoke signatures. The .NET marshaller will pass the underlying handle (IntPtr) instead. This is extremely useful because it avoids a number of problems: for example the finalizer could run when there are no active references to your object, but the underlying handle is still being used in a P/Invoke call. Using a SafeHandle directly in the P/Invoke signature makes the GC aware that the handle is used during the lifetime of the P/Invoke call, and it'll avoid finalizing the object until the call is complete.

                  G Offline
                  G Offline
                  Gordon Kushner
                  wrote on last edited by
                  #8

                  When passing a StringBuilder, the .NET marshaller passes a pointer to the StringBuilder's internal buffer. This is the voodoo that had me baffled the most. To the .NET developer, the StringBuilder is a sophisticated managed class, but somehow the API is able to treat is like a char* (I think). Thank you!

                  1 Reply Last reply
                  0
                  • D DaveyM69

                    Luc's given you a brilliant answer! In my experience, anything that takes the same memory footprint can be used. This makes int/uint (4 bytes), or even long/Size (8 bytes) interchangable. In the case of pointers, any reference type (or value type if used with ref [or out depending on the situation]) will work as it's the pointer to the object that's passed not the value pointed to. Whether the value stored at the address can be understood on the native side or managed side once returned is another matter of course!

                    Dave

                    If this helped, please vote & accept answer!

                    Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum.(Pete O'Hanlon)
                    BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

                    G Offline
                    G Offline
                    Gordon Kushner
                    wrote on last edited by
                    #9

                    Thank you, Dave.

                    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