diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+### 0.24.7
+
++ Add a mechanism for marshalling generic Haskell values into `GValue`s.
+
 ### 0.24.4
 
 + Add a workaround for old hsc2hs versions, so drop the constraint on hsc2hs.
diff --git a/Data/GI/Base/GType.hsc b/Data/GI/Base/GType.hsc
--- a/Data/GI/Base/GType.hsc
+++ b/Data/GI/Base/GType.hsc
@@ -24,6 +24,7 @@
     , gtypeParam
 
     , gtypeStablePtr
+    , gtypeHValue
     ) where
 
 import Data.GI.Base.BasicTypes (GType(..), CGType)
@@ -136,6 +137,12 @@
 -- | The `GType` for boxed `StablePtr`s.
 gtypeStablePtr :: GType
 gtypeStablePtr = GType haskell_gi_StablePtr_get_type
+
+foreign import ccall haskell_gi_HaskellValue_get_type :: CGType
+
+-- | The `GType` for a generic Haskell value.
+gtypeHValue :: GType
+gtypeHValue = GType haskell_gi_HaskellValue_get_type
 
 foreign import ccall "g_error_get_type" g_error_get_type :: CGType
 
diff --git a/Data/GI/Base/GValue.hs b/Data/GI/Base/GValue.hs
--- a/Data/GI/Base/GValue.hs
+++ b/Data/GI/Base/GValue.hs
@@ -29,6 +29,9 @@
     , unpackGValueArrayWithLength
     , mapGValueArrayWithLength
 
+    -- * Packing Haskell values into GValues
+    , HValue(..)
+
     -- * Setters and getters
     , set_object
     , get_object
@@ -45,6 +48,8 @@
     , take_stablePtr
     , set_param
     , get_param
+    , set_hvalue
+    , get_hvalue
     ) where
 
 import Control.Monad.IO.Class (MonadIO, liftIO)
@@ -58,8 +63,12 @@
                         CLong(..), CULong(..))
 import Foreign.C.String (CString)
 import Foreign.Ptr (Ptr, nullPtr, plusPtr, FunPtr)
-import Foreign.StablePtr (StablePtr, castStablePtrToPtr, castPtrToStablePtr)
+import Foreign.StablePtr (StablePtr, castStablePtrToPtr, castPtrToStablePtr,
+                          newStablePtr, deRefStablePtr)
+import Type.Reflection (typeRep, TypeRep)
+import System.IO (hPutStrLn, stderr)
 
+import Data.Dynamic (toDyn, fromDynamic, Typeable)
 import Data.GI.Base.BasicTypes
 import Data.GI.Base.BasicConversions (cstringToText, textToCString)
 import Data.GI.Base.GType
@@ -258,6 +267,11 @@
   gvalueSet_ = set_param
   gvalueGet_ = get_param
 
+instance Typeable a => IsGValue (HValue a) where
+  gvalueGType_ = return gtypeHValue
+  gvalueSet_ = set_hvalue
+  gvalueGet_ = get_hvalue
+
 foreign import ccall "g_value_set_string" _set_string ::
     Ptr GValue -> CString -> IO ()
 foreign import ccall "g_value_get_string" _get_string ::
@@ -404,11 +418,12 @@
 set_stablePtr :: Ptr GValue -> StablePtr a -> IO ()
 set_stablePtr gv ptr = set_boxed gv (castStablePtrToPtr ptr)
 
-foreign import ccall g_value_take_boxed :: Ptr GValue -> StablePtr a -> IO ()
+foreign import ccall g_value_take_boxed :: Ptr GValue -> Ptr a -> IO ()
 
 -- | Like `set_stablePtr`, but the `GValue` takes ownership of the `StablePtr`
 take_stablePtr :: Ptr GValue -> StablePtr a -> IO ()
-take_stablePtr = g_value_take_boxed
+take_stablePtr gvPtr stablePtr =
+  g_value_take_boxed gvPtr (castStablePtrToPtr stablePtr)
 
 -- | Get the value of a `GValue` containing a `StablePtr`
 get_stablePtr :: Ptr GValue -> IO (StablePtr a)
@@ -482,3 +497,42 @@
     else do
     fPtr <- g_param_spec_ref ptr >>= newManagedPtr' ptr_to_g_param_spec_unref
     return . Just $! GParamSpec fPtr
+
+-- | A type isomorphic to `Maybe a`, used to indicate to
+-- `fromGValue`/`toGValue` that we are packing a native Haskell value,
+-- without attempting to marshall it to the corresponding C type.
+data HValue a = HValue a -- ^ A packed value of type `a`
+              | NoHValue -- ^ An empty `HValue`
+  deriving (Show, Eq)
+
+-- | Set the `GValue` to the given Haskell value.
+set_hvalue :: Typeable a => Ptr GValue -> HValue a -> IO ()
+set_hvalue gvPtr NoHValue = set_boxed gvPtr nullPtr
+set_hvalue gvPtr (HValue v) = do
+  sPtr <- newStablePtr (toDyn v)
+  g_value_take_boxed gvPtr (castStablePtrToPtr sPtr)
+
+-- | Get the value in the GValue, checking that the type is
+-- `gtypeHValue`. Will return NULL and print a warning if the GValue
+-- is of the wrong type.
+foreign import ccall "haskell_gi_safe_get_boxed_haskell_value" safe_get_boxed_hvalue ::
+    Ptr GValue -> IO (Ptr b)
+
+-- | Read the Haskell value of the given type from the `GValue`. If
+-- the `GValue` contains no value of the expected type, `NoHValue`
+-- will be returned instead, and an error will be printed to stderr.
+get_hvalue :: forall a. Typeable a => Ptr GValue -> IO (HValue a)
+get_hvalue gvPtr = do
+  hvaluePtr <- safe_get_boxed_hvalue gvPtr
+  if hvaluePtr == nullPtr
+    then return NoHValue
+    else do
+      dyn <- deRefStablePtr (castPtrToStablePtr hvaluePtr)
+      case fromDynamic dyn of
+        Nothing -> do
+          hPutStrLn stderr ("HASKELL-GI: Unexpected type ‘" <> show dyn
+                             <> "’ inside the GValue at ‘" <> show gvPtr
+                             <> "’.\n\tExpected ‘" <> show (typeRep :: TypeRep a)
+                             <> "’.")
+          return NoHValue
+        Just val -> return (HValue val)
diff --git a/csrc/hsgclosure.c b/csrc/hsgclosure.c
--- a/csrc/hsgclosure.c
+++ b/csrc/hsgclosure.c
@@ -383,6 +383,42 @@
   return g_define_type_id;
 }
 
+/* This is identical to haskell_gi_StablePtr_get_type, other than the
+   type name. The reason for this is that we want two different types,
+   to distinguish between GValues wrapping generic StablePtrs, and
+   those wrapping specifically wrapping StablePtrs to Dynamic
+   values. */
+GType haskell_gi_HaskellValue_get_type (void)
+{
+  static gsize g_define_type_id = 0;
+
+  if (g_once_init_enter (&g_define_type_id))
+    {
+      GType type_id =
+        g_boxed_type_register_static (g_intern_static_string ("HaskellGIHaskellValue"),
+                                      duplicateStablePtr,
+                                      hs_free_stable_ptr);
+
+      g_once_init_leave (&g_define_type_id, type_id);
+    }
+
+  return g_define_type_id;
+}
+
+/* A safer version of get_boxed, that checks that the GValue contains
+   the right boxed type. */
+gpointer haskell_gi_safe_get_boxed_haskell_value(const GValue *gv)
+{
+  if (G_VALUE_TYPE(gv) != haskell_gi_HaskellValue_get_type()) {
+    fprintf(stderr, "Unexpected type inside the GValue: ‘%s’\n.",
+            G_VALUE_TYPE_NAME(gv));
+
+    return NULL;
+  }
+
+  return g_value_get_boxed(gv);
+}
+
 /* Release the FunPtr allocated for a Haskell signal handler */
 void
 haskell_gi_release_signal_closure (gpointer unused,
diff --git a/haskell-gi-base.cabal b/haskell-gi-base.cabal
--- a/haskell-gi-base.cabal
+++ b/haskell-gi-base.cabal
@@ -1,5 +1,5 @@
 name:                haskell-gi-base
-version:             0.26.6
+version:             0.26.7
 synopsis:            Foundation for libraries generated by haskell-gi
 description:         Foundation for libraries generated by haskell-gi
 homepage:            https://github.com/haskell-gi/haskell-gi
