Вариант 2 довольно нормальный, например, метод Release (), который просто «удаляет это».
Обратите внимание, что обычно DLL не имеет собственной кучи как таковой - большинство DLL будут делиться кучей процессов (см. GetProcessHeap), но часто среда выполнения языка будет украшать или добавлять прописку вокруг выделенной памяти - так что указатель, на котором заканчивается ваша dll, - это не тот же указатель, который куча будет ожидать в свободном вызове.
Общее правило всегда освобождает выделенную память с помощью свободной функции, изнутри того же модуля.
Однако есть некоторые исключения - память, которую вы знаете, была выделена непосредственно с HeapAlloc из известной кучи, может быть освобождена от другого модуля. GlobalAlloc / GlobalFree работает через модули. Строки, выделенные SysAllocString, могут быть освобождены от другого модуля. Память, которую вы знаете, была выделена той же DLL-версией среды выполнения, может быть освобождена от другого модуля, но я бы держался подальше от этого.