Всем спасибо, все свободны. Проблема была в другом месте. На всякий случай, вот такой код, похоже, работает.
[DllImport("some_lib.dll", EntryPoint = "some_func", ...)]
private static extern int x_SomeFunc(ref IntPtr str);
public static int SomeFunc(out string str)
{
IntPtr ptr = IntPtr.Zero;
int ret = x_SomeFunc(ref ptr);
// lstrlenA() нужно импортировать из kernel32. Или использовать что-то еще для вычисления размера буфера.
int size = lstrlenA(ptr);
byte[] bytes = new byte[size];
Marshal.Copy(ptr, bytes, 0, size);
str = Encoding.UTF8.GetString(bytes); // UTF8 - это в моем случае
// освободить память, если строка была создана динамически
...
return ret;
}