gpt4 book ai didi

c# - PInvoke DllExport : structure marshaling failure

转载 作者:太空宇宙 更新时间:2023-11-03 13:14:30 26 4
gpt4 key购买 nike

我在编码和 PInvoke 方面遇到了一些问题,我必须为现有的 native 应用程序(Oracle Siebel CRM,调用中心集成接口(interface))开发某种驱动程序。从我的角度来看,应用程序的来源是黑盒子。这是两个结构的签名(如 API 描述中所定义)和使用它们的 API 函数:

struct ISC_KeyValue /* Key-Value element */
{
ISC_STRING paramName;
ISC_STRING paramValue;
};

struct ISC_KVParamList /* List of Key-Value parameter */
{
struct ISC_KeyValue* dataItems;
long len;
};

ISCAPI ISC_RESULT CreateISCDriverInstance
/* in */(const ISC_STRING mediaTypeStr,
/* in */ const ISC_STRING languageCode,
/* in */ const ISC_STRING connectString,
/* in */ const struct ISC_KVParamList* datasetParams,
/* out */ ISC_DRIVER_HANDLE* handle);

这里的 ISC_STRING 是宽字符 C++ 字符串(2 字节 Unicode)。 ISC_RESULT 是类似于 HRESULT 类型的整数值。我在将 ISC_KVParamList* datasetParams 编码到 CreateISCDriverInstance 函数时遇到问题,我需要帮助。

这是我对如何整理它的看法:

   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IscKeyValue
{
[MarshalAs(UnmanagedType.LPWStr)]
public string ParamName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ParamValue;
}

[StructLayout(LayoutKind.Sequential)]
public struct IscKvParamList
{

public IntPtr DataItems;
[MarshalAs(UnmanagedType.I4)]
public int Len;
}

以及函数本身:

    [DllExport("CreateISCDriverInstance", CallingConvention = CallingConvention.StdCall)]
public static int CreateIscDriverInstance([MarshalAs(UnmanagedType.LPWStr)] string mediaTypeStr,
[In,MarshalAs(UnmanagedType.LPWStr)] string languageCode,
[In,MarshalAs(UnmanagedType.LPWStr)] string connectString,
[In, Out] ref IscKvParamList dataParams, [Out] IntPtr handle)
{
var datasetParams =(IscKvParamList)Marshal.PtrToStructure(dataParams,typeof(IscKvParamList));
// Some code here
}

当我尝试执行 PtrToStructure 转换时,出现异常,提示“结构不能是值类。”

问题:如何改进 dataParams 参数的编码

附注额外细节 :1.我确定字符集是Unicode2. 我不知道数组的大小,我不能按值编码它。3. 另外,我有很久以前用 Delphi 编写的此类驱动程序的示例实现:

TNamedParam = record 
Name: WideString;
Value: WideString;
end;

TNamedParamList = record
Params: packed array of TNamedParam;
Count: Cardinal;
end;

function CreateISCDriverInstance(const AMediaTypeStr, ALanguageCode, AConnectString: PWideChar; const AParams: TISCNamedParamList; out ADriverHandle: THandle):
HRESULT; begin try ADriverHandle := icISCCommunicationDriver.CreateInstance(AParams).Handle;
Result := SC_EC_OK;
except Result := SC_EC_DRIVER_CREATION_ERR;
end;
end;

以及 AParams 的解析:

procedure JoinISCNamedParamList(const AParamList: TNamedParamList; var LParamList: TISCNamedParamList);
var i:Integer;
begin
LParamList.Count := AParamList.Count;
SetLength(LParamList.Params, LParamList.Count);
for I := 0 to Pred(AParamList.Count) do
begin
LParamList.Params[i].Name := PWideChar(AParamList.Params[i].Name);
LParamList.Params[i].Value := PWideChar(AParamList.Params[i].Value);
end;
end;

在这里可以说结构的布局是顺序的。

最佳答案

问题中的信息不完整。网络搜索显示如下代码:

typedef wchar_t ISC_CHAR
typedef ISC_CHAR* ISC_STRING;

这与您在问题中指出的相符。所以,让我们开始吧。但是,如果您可以提供可以在您的某个头文件中找到的信息,那就更好了。

天真地你的结构应该是:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IscKeyValue
{
public string ParamName;
public string ParamValue;
}

[StructLayout(LayoutKind.Sequential)]
public struct IscKvParamList
{
public IscKeyValue[] DataItems;
public int Len;
}

不需要您使用的 MarshalAs,因为默认编码就足够了。

但是编码器不会处理结构中的数组。所以我认为你需要这样做:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct IscKeyValue
{
public IntPtr ParamName;
public IntPtr ParamValue;
}

[StructLayout(LayoutKind.Sequential)]
public struct IscKvParamList
{
public IntPtr DataItems;
public int Len;
}

至于函数,翻译为:

[DllExport("CreateISCDriverInstance", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode, ExactSpelling = true)]
public static int CreateIscDriverInstance(
string mediaTypeStr,
string languageCode,
string connectString,
[In] ref IscKvParamList dataParams,
out IntPtr handle
);

现在是乐趣。准备 IscKvParamList 参数。

IscKeyValue 数组开始:

IscKeyValue[] KeyValueArr = new IscKeyValue[...];
for (int i = 0; i < KeyValueArr.Length; i++)
{
KeyValueArr[i].ParamName = Marshal.StringToCoTaskMemUni(...);
KeyValueArr[i].ParamValue = Marshal.StringToCoTaskMemUni(...);
}

现在是 IscKvParamList。我想我会固定阵列。像这样:

GCHandle ArrHandle = GCHandle.Alloc(KeyValueArr, GCHandleType.Pinned);
try
{
IscKvParamList dataParams;
dataParams.DataItems = ArrHandle.AddrOfPinnedObject();
dataParams.Len = KeyValueArr.Length;
int retval = CreateIscDriverInstance(
mediaTypeStr,
languageCode,
connectString,
ref dataParams,
out handle
);
}
finally
{
ArrHandle.Free();
}

您必须确保记得释放在调用 Marshal.StringToCoTaskMemUni 时分配的非托管内存。

关于c# - PInvoke DllExport : structure marshaling failure,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27034263/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com