C# 封送CTL_使用

C# 封送CTL_使用,c#,interop,marshalling,C#,Interop,Marshalling,我正在做一个使用证书的项目,我需要将CTL#U使用结构转换为C#。原结构如下: typedef struct _CTL_USAGE { DWORD cUsageIdentifier; LPSTR *rgpszUsageIdentifier; } CTL_USAGE, *PCTL_USAGE, CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE; //EnhancedUsage is a List<String> containing the Enh

我正在做一个使用证书的项目,我需要将CTL#U使用结构转换为C#。原结构如下:

typedef struct _CTL_USAGE {
  DWORD cUsageIdentifier;
  LPSTR *rgpszUsageIdentifier;
} CTL_USAGE, *PCTL_USAGE, CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE;
//EnhancedUsage is a List<String> containing the Enhanced Usage OIDs
CTL_USAGE usage = new CTL_USAGE()
{
    cUsageIdentifier = EnhancedUsage.Count,
};

List<IntPtr> usageList = new List<IntPtr>();

foreach (string s in EnhancedUsage)
    usageList.Add(new PinnedHandle(Encoding.ASCII.GetBytes(s)));

usage.rgpszUsageIdentifier = new PinnedHandle(usageList.ToArray());

IntPtr data = Marshal.AllocHGlobal(Marshal.SizeOf(usage));
Marshal.StructureToPtr(usage, data, false);

int encodedSize = 0;

if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, null, ref encodedSize))
    throw new Win32Exception();

byte[] buffer = new byte[encodedSize];

if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, buffer, ref encodedSize))
    throw new Win32Exception();
根据p/Invoke网站,C#结构应为:

[StructLayout(LayoutKind.Sequential)]
public struct CTL_USAGE
{
    public int cUsageIdentifier;
    public IntPtr rgpszUsageIdentifier;
}
为了使用这段代码,我使用Encoding.ASCII.GetBytes()将要添加到结构中的每个字符串转换为字节数组。然后,我使用GCHandle.Alloc(byteArray,GCHandleType.pinted)将该字节数组转换为GCHandle。我将该值添加到IntPtr数组中,然后为IntPtr数组创建GCHandle并将其分配给rgpszUsageIdentifier。对CryptEncodeObjectEx的调用不会引发错误,但生成的数据是垃圾,不能使用CryptDecodeObject取消加密。我的编码如下:

typedef struct _CTL_USAGE {
  DWORD cUsageIdentifier;
  LPSTR *rgpszUsageIdentifier;
} CTL_USAGE, *PCTL_USAGE, CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE;
//EnhancedUsage is a List<String> containing the Enhanced Usage OIDs
CTL_USAGE usage = new CTL_USAGE()
{
    cUsageIdentifier = EnhancedUsage.Count,
};

List<IntPtr> usageList = new List<IntPtr>();

foreach (string s in EnhancedUsage)
    usageList.Add(new PinnedHandle(Encoding.ASCII.GetBytes(s)));

usage.rgpszUsageIdentifier = new PinnedHandle(usageList.ToArray());

IntPtr data = Marshal.AllocHGlobal(Marshal.SizeOf(usage));
Marshal.StructureToPtr(usage, data, false);

int encodedSize = 0;

if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, null, ref encodedSize))
    throw new Win32Exception();

byte[] buffer = new byte[encodedSize];

if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, buffer, ref encodedSize))
    throw new Win32Exception();

我不认为这会造成任何问题,因为我在其他类似的情况下使用过它,它们工作正常。您知道如何使其正常工作吗?

使用
AllocHGLobal
分配非托管内存块,将
IntPtr
实例从
usageList
复制到它,并将此块句柄传递给非托管函数。为此使用
Marshal.WriteIntPtr方法(IntPtr、Int32、IntPtr)


在我的互操作性代码中,我避免固定托管对象,而是更喜欢使用不同的封送处理方法构建本机内存区域。

您只能对具有已知内部结构的简单对象使用AddrOfPinnedObject,行字节[]。在这里,您将AddrOfPinnedObject应用于
列表
,这是错误的。非托管函数只读取一些.NET类内部。与此相反,使用
AllocHGLobal
分配非托管内存块,将
usageList
中的所有IntPtr实例复制到它,并将此块句柄传递给非托管函数。为此使用
Marshal.WriteIntPtr方法(IntPtr,Int32,IntPtr)
。实际上,我使用的是List.ToArray()上的AddrOfPinnedObject。一旦它被转换成一个数组,它就工作了。好的,我明白了。问题出在其他地方。rgpszUsageIdentifier:CTL扩展的对象标识符(OID)数组。我不知道什么是OID,也许你只是传递了错误的数据?