我已经解决了完全相同的问题 - 为 .NET 包装 libspotify。我采用了 IntPtr 路线,并编写了一个辅助类来进行编组。我发现数组编组非常令人抓狂,不值得为之撕心裂肺。
这是助手类:
https://github.com/openhome/ohLibSpotify/blob/master/src/ohLibSpotify/Utf8String.cs#L99 https://github.com/openhome/ohLibSpotify/blob/master/src/ohLibSpotify/Utf8String.cs#L99
简化版:
internal class Utf8String : IDisposable
{
IntPtr iPtr;
public IntPtr IntPtr { get { return iPtr; } }
public int BufferLength { get { return iBufferSize; } }
int iBufferSize;
public Utf8String(string aValue)
{
if (aValue == null)
{
iPtr = IntPtr.Zero;
}
else
{
byte[] bytes = Encoding.UTF8.GetBytes(aValue);
iPtr = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, iPtr, bytes.Length);
Marshal.WriteByte(iPtr, bytes.Length, 0);
iBufferSize = bytes.Length + 1;
}
}
public void Dispose()
{
if (iPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(iPtr);
iPtr = IntPtr.Zero;
}
}
}
用法可以在这里看到:
https://github.com/openhome/ohLibSpotify/blob/master/src/ohLibSpotify/SpotifySession.cs#L104 https://github.com/openhome/ohLibSpotify/blob/master/src/ohLibSpotify/SpotifySession.cs#L104
public static SpotifySession Create(SpotifySessionConfig config)
{
IntPtr sessionPtr = IntPtr.Zero;
IntPtr listenerToken;
using (var cacheLocation = SpotifyMarshalling.StringToUtf8(config.CacheLocation))
using (var settingsLocation = SpotifyMarshalling.StringToUtf8(config.SettingsLocation))
using (var userAgent = SpotifyMarshalling.StringToUtf8(config.UserAgent))
using (var deviceId = SpotifyMarshalling.StringToUtf8(config.DeviceId))
using (var proxy = SpotifyMarshalling.StringToUtf8(config.Proxy))
using (var proxyUsername = SpotifyMarshalling.StringToUtf8(config.ProxyUsername))
using (var proxyPassword = SpotifyMarshalling.StringToUtf8(config.ProxyPassword))
using (var traceFile = SpotifyMarshalling.StringToUtf8(config.TraceFile))
{
IntPtr appKeyPtr = IntPtr.Zero;
listenerToken = ListenerTable.PutUniqueObject(config.Listener, config.UserData);
try
{
NativeCallbackAllocation.AddRef();
byte[] appkey = config.ApplicationKey;
appKeyPtr = Marshal.AllocHGlobal(appkey.Length);
Marshal.Copy(config.ApplicationKey, 0, appKeyPtr, appkey.Length);
sp_session_config nativeConfig = new sp_session_config {
api_version = config.ApiVersion,
cache_location = cacheLocation.IntPtr,
settings_location = settingsLocation.IntPtr,
application_key = appKeyPtr,
application_key_size = (UIntPtr)appkey.Length,
user_agent = userAgent.IntPtr,
callbacks = SessionDelegates.CallbacksPtr,
userdata = listenerToken,
compress_playlists = config.CompressPlaylists,
dont_save_metadata_for_playlists = config.DontSaveMetadataForPlaylists,
initially_unload_playlists = config.InitiallyUnloadPlaylists,
device_id = deviceId.IntPtr,
proxy = proxy.IntPtr,
proxy_username = proxyUsername.IntPtr,
proxy_password = proxyPassword.IntPtr,
tracefile = traceFile.IntPtr,
};
// Note: sp_session_create will invoke a callback, so it's important that
// we have already done ListenerTable.PutUniqueObject before this point.
var error = NativeMethods.sp_session_create(ref nativeConfig, ref sessionPtr);
SpotifyMarshalling.CheckError(error);
}
catch
{
ListenerTable.ReleaseObject(listenerToken);
NativeCallbackAllocation.ReleaseRef();
throw;
}
finally
{
if (appKeyPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(appKeyPtr);
}
}
}
SpotifySession session = SessionTable.GetUniqueObject(sessionPtr);
session.Listener = config.Listener;
session.UserData = config.UserData;
session.ListenerToken = listenerToken;
return session;
}
因为手动编写这些东西很快就会变得乏味,所以绝大多数包装器和 DllImport 声明都是从 api.h 头文件自动生成的。 (因此,您不会在 github 存储库中找到它们,您需要下载项目并构建它才能查看所有生成的代码。)
它都是自由许可的(2-clause BSD),所以您可以随意使用它或借用它的一部分。