背景
我正在尝试基于修改后的 libspotify.net ( http://libspotifydotnet.codeplex.com/ ) 编写高级 libspotify 包装器。由于 libspotify.net 只是一个薄的(并且完全被窃听......)pinvoke 层,它不处理 libspotify 使用的 utf8 编码。
我的想法是将字符串转换为 byte[] 并适当地更改签名。
我有这个原生结构:
typedef struct sp_session_config {
int api_version;
const char *cache_location;
const char *settings_location;
const void *application_key;
size_t application_key_size;
const char *user_agent;
const sp_session_callbacks *callbacks;
void *userdata;
bool compress_playlists;
bool dont_save_metadata_for_playlists;
bool initially_unload_playlists;
const char *device_id;
const char *proxy;
const char *proxy_username;
const char *proxy_password;
const char *ca_certs_filename;
const char *tracefile;
} sp_session_config;
工作版本:
[DllImport("libspotify")]
public static extern sp_error sp_session_create(ref sp_session_config config, out sp_session sessionPtr);
public struct sp_session_config
{
public int api_version;
public IntPtr cache_location;
public IntPtr settings_location;
public IntPtr application_key;
public uint application_key_size;
public IntPtr user_agent;
public IntPtr callbacks;
public IntPtr userdata;
public bool compress_playlists;
public bool dont_save_metadata_for_playlists;
public bool initially_unload_playlists;
public IntPtr device_id;
public IntPtr proxy;
public IntPtr proxy_username;
public IntPtr proxy_password;
public IntPtr ca_certs_filename;
public IntPtr tracefile;
}
此版本将工作交给使用该库的开发人员。
我的版本:
public struct sp_session_config_internal
{
public int api_version;
public byte[] cache_location;
public byte[] settings_location;
public byte[] application_key;
public uint application_key_size;
public byte[] user_agent;
public IntPtr callbacks;
public IntPtr userdata;
public bool compress_playlists;
public bool dont_save_metadata_for_playlists;
public bool initially_unload_playlists;
public byte[] device_id;
public byte[] proxy;
public byte[] proxy_username;
public byte[] proxy_password;
public byte[] ca_certs_filename;
public byte[] tracefile;
}
[DllImport("libspotify", EntryPoint="sp_session_create")]
private static extern sp_error sp_session_create_internal(ref sp_session_config_internal config, out sp_session sessionPtr);
public static sp_error sp_session_create(ref sp_session_config config, out sp_session sessionPtr)
{
sp_session_config_internal config_internal = new sp_session_config_internal();
config_internal.api_version = config.api_version;
config_internal.application_key = config.application_key;
config_internal.application_key_size = (uint)config.application_key.Length;
config_internal.ca_certs_filename = SH.StringToUtf8Bytes( config.ca_certs_filename);
...
var err = sp_session_create_internal(ref config_internal, out session);
...
}
运行时,这会在 libspotify 中出现以下错误:访问冲突读取位置 0x000001c0 我用谷歌搜索并读取了有时只有第一个数组元素被复制的地方。我试着用
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)]
这给出了异常“无法封送类型'sp_session_config_internal'的字段'cache_location':托管/非托管类型组合无效(数组字段必须与ByValArray或SafeArray配对)。”
tl;博士
使用 IntPtr 和手动编组的复杂解决方案有效,使用 utf8 字符串的字节数组的更简单解决方案在库读取时会导致访问冲突。有没有比使用 intptr 手动编组更简单的方法?