C# .NET && C++ a Data Marshaling

Programovacie jazyky, rady, poradňa...
Mek
Addict
Addict
Používateľov profilový obrázok
Príspevky: 4661
Registrovaný: 23 mar 2005, 23:00
Bydlisko: ZA <-> TN
Kontaktovať používateľa:

C# .NET && C++ a Data Marshaling

Príspevok od používateľa Mek »

Zdravicko,
v skratke: mam DLL kniznicu naprogramovanu v C++, a potrebujem v C# pouzit jednu funkciu z nej. Docital som sa na nete, ze potrebujem Data Marshaling, coz sa zda pomerne jednoduche, avsak nikde neprebrali specialne pripady (ako je ten moj), ked potrebujem naplnit datami struct.
Hlavicka danej funkcie v C++ je takato:

Kód: Vybrať všetko

int cert_get_info(const unsigned char *pcert, unsigned int lcert, T_CERT_INFO *info);
a struct T_CERT_INFO vyzera takto:

Kód: Vybrať všetko

typedef struct
  {
    unsigned int lcert;
    char *subject;
    unsigned int l_subject;
    char *issuer;
    unsigned int l_issuer;
    char *mod;
    unsigned int l_mod;
    char *exp;
    unsigned int l_exp;
    char *pkinfo;
    unsigned int l_pkinfo;
    char *serial;
    unsigned int l_serial;
    char *validfrom;
    unsigned int l_validfrom;
    char *validto;
    unsigned int l_validto;
  } T_CERT_INFO;
V C# som si napisal takuto triedu, ktora by mala posunut danej funkcii v tej DLL kniznici
1. surove data
2. dlzku tychto dat
3. objekt CERTINFO, kam by sa mali naplnit jednotlive polia zo struct-u. Prave tu som si nie isty, ako spravne takyto struct (resp. pointer na struct) marshal-ovat.

Kód: Vybrať všetko

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace PKCS11Lib
{
    public static class Asn1Parser
    {
        public static byte[] certData;
        public static CERTINFO certInfo;

        [DllImport("asn1.dll", EntryPoint = "cert_get_info")]
        private static extern void Cert_get_info([In] byte[] in_certdata, [In] uint in_datalen, [MarshalAs(UnmanagedType.LPStruct)] [In][Out] ref CERTINFO out_pinfo);

        public static void LoadInfo(string rawData)
        {
            certInfo = new CERTINFO();
            certData = Encoding.ASCII.GetBytes(rawData);
            Cert_get_info(certData, Convert.ToUInt32(certData.Length), ref certInfo); //////////////////////////// tuna to pada
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public class CERTINFO
    {
        public uint lcert;
        [MarshalAs(UnmanagedType.LPStr)]
        public string subject;
        public uint l_subject;
        [MarshalAs(UnmanagedType.LPStr)]
        public string issuer;
        public uint l_issuer;
        [MarshalAs(UnmanagedType.LPStr)]
        public string mod;
        public uint l_mod;
        [MarshalAs(UnmanagedType.LPStr)]
        public string exp;
        public uint l_exp;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pkinfo;
        public uint l_pkinfo;
        [MarshalAs(UnmanagedType.LPStr)]
        public string serial;
        public uint l_serial;
        [MarshalAs(UnmanagedType.LPStr)]
        public string validfrom;
        public uint l_validfrom;
        [MarshalAs(UnmanagedType.LPStr)]
        public string validto;
        public uint l_validto;
    }
}
Tuto triedu potom pouzivam takto:

Kód: Vybrať všetko

Asn1Parser.LoadInfo(Encoding.ASCII.GetString(cert.GetRawCertData(), 0, cert.GetRawCertData().Length));
a z clena tejto triedy certInfo by som si vytiahol pozadovane udaje v tvare triedy CERTINFO, ak by to ofsem fungovalo. (premenna cert je typu X509Certificate a obsahuje platny certifikat).
Moja otazka je, ako korektne marshalovat data do a z tejto funkcie v DLL kniznici? Najma co sa tyka toho struct-u. Skusal som tam namiesto ref-u aj out, potom aj rozne menit atributy volania [In] a [Out] a nic nezabralo. Stale mi to pada na oznacenom riadku s exception NotSupportedException, ktora ziadne blizsie info ani popis neponuka.
Topiaci sa aj stebla chyta, takze... vie niekto pomoct, pls?
harrison314
Hardcore addict
Hardcore addict
Používateľov profilový obrázok
Príspevky: 8224
Registrovaný: 27 máj 2009, 20:42
Bydlisko: Bratislava
Kontaktovať používateľa:

Re: C# .NET && C++ a Data Marshaling

Príspevok od používateľa harrison314 »

Maniesto tej triedy struktury pouzi toto

Kód: Vybrať všetko

unsafe struct  T_CERT_INFO
  {
    uint lcert;
    char *subject;
    uint l_subject;
    char *issuer;
    uint l_issuer;
    char *mod;
    uint  l_mod;
    char *exp;
    uint  l_exp;
    char *pkinfo;
    uint  l_pkinfo;
    char *serial;
    uint  l_serial;
    char *validfrom;
    uint  l_validfrom;
    char *validto;
    uint  l_validto;
  }
String z ukazovatela zskas ( kod musi byt v bloku unsafe):

Kód: Vybrať všetko

unsafe{
char *ps = ......
string s = new string(ps);
}
A namiesto priznakov [In] a [Out] pouzi len klasicke ref, mozno to nieje idelane ale funguje na 100%.
alebo mozes dleklarovat funkciu z DLL aj
:

Kód: Vybrať všetko

[DllImport("asn1.dll", EntryPoint = "cert_get_info")]
private unsafe static extern int cert_get_info(const unsigned char *pcert, unsigned int lcert, ref T_CERT_INFO info);
Mek
Addict
Addict
Používateľov profilový obrázok
Príspevky: 4661
Registrovaný: 23 mar 2005, 23:00
Bydlisko: ZA <-> TN
Kontaktovať používateľa:

Re: C# .NET && C++ a Data Marshaling

Príspevok od používateľa Mek »

jeeej dakujem pekne za pomoc... s pomocou tvojho prispevku a trochy googlovania som sa dostal k takemuto kodu:

Kód: Vybrať všetko

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace PKCS11Lib
{
    public static class Asn1Parser
    {
        public static string certData;
        public static T_CERT_INFO certInfo;
        public static string issuer;
        public static string serial;
        public static string subject;

        [DllImport("asn1.dll", EntryPoint = "cert_get_info")]
        private unsafe static extern void Cert_get_info(char *pcert, uint lcert, ref T_CERT_INFO info);

        public static void LoadInfo(string rawData)
        {
            certInfo = new T_CERT_INFO();
            certData = rawData;
            char[] copied = new char[10240];
            certData.CopyTo(0, copied, 0, certData.Length);
            unsafe
            {
                fixed(char *p=copied)Cert_get_info(p, Convert.ToUInt32(certData.Length), ref certInfo);
                issuer = new string(certInfo.issuer);
                serial = new string(certInfo.serial);
                serial = new string(certInfo.subject);
            }
        }
    }

    public unsafe struct T_CERT_INFO
    {
        public uint lcert;
        public char* subject;
        public uint l_subject;
        public char* issuer;
        public uint l_issuer;
        public char* mod;
        public uint l_mod;
        public char* exp;
        public uint l_exp;
        public char* pkinfo;
        public uint l_pkinfo;
        public char* serial;
        public uint l_serial;
        public char* validfrom;
        public uint l_validfrom;
        public char* validto;
        public uint l_validto;
    }
}
Funguje perfektne. Ka :plus: :mrgreen:

//autoeditácia príspevku (23 Sep 2010, 18:10)
Hm, tak som sa mylil, v skutocnosti nic nefunguje. Vsetky atributy tej struktury ostavaju prazdne... Budem sa s tym musiet trochu viac pohrat :|

//autoeditácia príspevku (23 Sep 2010, 20:52)
Tak uz to funguje, v takejto konecnej podobe:

Kód: Vybrať všetko

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections;
using System.IO;

namespace PKCS11Lib
{
    public static class Asn1Parser
    {
        public static byte[] certData;
        private static T_CERT_INFO certInfo;
        private static byte[] issuer;
        private static byte[] serial;
        private static byte[] subject;
        private static byte[] mod;
        private static byte[] exp;
        private static byte[] pkinfo;
        private static byte[] validfrom;
        private static byte[] validto;

        public static byte[] Issuer
        {
            get { return Asn1Parser.issuer; }
        }

        public static byte[] Serial
        {
            get { return Asn1Parser.serial; }
        }

        public static byte[] Subject
        {
            get { return Asn1Parser.subject; }
        }

        public static byte[] Mod
        {
            get { return Asn1Parser.mod; }
        }

        public static byte[] Exp
        {
            get { return Asn1Parser.exp; }
        }

        public static byte[] Pkinfo
        {
            get { return Asn1Parser.pkinfo; }
        }

        public static byte[] Validfrom
        {
            get { return Asn1Parser.validfrom; }
        }

        public static byte[] Validto
        {
            get { return Asn1Parser.validto; }
        }

        [DllImport("Asn1.dll", EntryPoint = "cert_get_info")]
        private unsafe static extern void Cert_get_info(byte* pcert, uint lcert, T_CERT_INFO* info);

        public static void LoadInfo(byte[] rawData)
        {
            certInfo = new T_CERT_INFO();
            certData = rawData;
            uint l = Convert.ToUInt32(certData.Length);
            unsafe
            {
                fixed (byte* p = certData)
                {
                    fixed (T_CERT_INFO* pi = &certInfo)
                    {
                        Cert_get_info(p, l, pi);

                        issuer = new byte[certInfo.l_issuer];
                        for (uint i = 0; i < certInfo.l_issuer; i++)
                        {
                            issuer[i] = certInfo.issuer[i];
                        }

                        serial = new byte[certInfo.l_serial];
                        for (uint i = 0; i < certInfo.l_serial; i++)
                        {
                            serial[i] = certInfo.serial[i];
                        }

                        subject = new byte[certInfo.l_subject];
                        for (uint i = 0; i < certInfo.l_subject; i++)
                        {
                            subject[i] = certInfo.subject[i];
                        }

                        mod = new byte[certInfo.l_mod];
                        for (uint i = 0; i < certInfo.l_mod; i++)
                        {
                            mod[i] = certInfo.mod[i];
                        }

                        exp = new byte[certInfo.l_exp];
                        for (uint i = 0; i < certInfo.l_exp; i++)
                        {
                            exp[i] = certInfo.exp[i];
                        }

                        pkinfo = new byte[certInfo.l_pkinfo];
                        for (uint i = 0; i < certInfo.l_pkinfo; i++)
                        {
                            pkinfo[i] = certInfo.pkinfo[i];
                        }

                        validfrom = new byte[certInfo.l_validfrom];
                        for (uint i = 0; i < certInfo.l_validfrom; i++)
                        {
                            validfrom[i] = certInfo.validfrom[i];
                        }

                        validto = new byte[certInfo.l_validto];
                        for (uint i = 0; i < certInfo.l_validto; i++)
                        {
                            validto[i] = certInfo.validto[i];
                        }
                    }
                }
            }
        }
    }

    public unsafe struct T_CERT_INFO
    {
        public uint lcert;
        public byte* subject;
        public uint l_subject;
        public byte* issuer;
        public uint l_issuer;
        public byte* mod;
        public uint l_mod;
        public byte* exp;
        public uint l_exp;
        public byte* pkinfo;
        public uint l_pkinfo;
        public byte* serial;
        public uint l_serial;
        public byte* validfrom;
        public uint l_validfrom;
        public byte* validto;
        public uint l_validto;
    }
}
harrison314
Hardcore addict
Hardcore addict
Používateľov profilový obrázok
Príspevky: 8224
Registrovaný: 27 máj 2009, 20:42
Bydlisko: Bratislava
Kontaktovať používateľa:

Re: C# .NET && C++ a Data Marshaling

Príspevok od používateľa harrison314 »

Myslim , ze C# by bolo blizsie keby danu funkciu este obalis v C++ na pouzitie referncii:

Kód: Vybrať všetko

extern "C" int cert_get_info_cs(const unsigned char &pcert, unsigned int lcert, T_CERT_INFO &info)
{
return cert_get_info(&pcert,lcert,&info);
}
A potom namiesto ukazovatelov a unsafe kodu mozes pouzit ref.

Ozaj a tie bytove polia boli stringy ? lebo sa mi to podla tvoho riesenia nezda, keby sa koncia nulov a neuvolnia sa z pameti predcasne, tak by moje riesenie malo fungovat.
Ja som robil podobne veci ( koli richlosti aj multiplatformosti ) a islo mi to aj z vyssou abstrakciu datvych typov ( char* na StringBuilder siel bez Marsalingu).
Mek
Addict
Addict
Používateľov profilový obrázok
Príspevky: 4661
Registrovaný: 23 mar 2005, 23:00
Bydlisko: ZA <-> TN
Kontaktovať používateľa:

Re: C# .NET && C++ a Data Marshaling

Príspevok od používateľa Mek »

StringBuilder sa marshaluje transparentne sam, rovnako ako string :) (tak som sa na nete docital).
K tym mojim bytovym poliam, nie, neboli to stringy; su to obycajne binarne data, preto som pouzil pole bytov. Nenasiel som ziadnu funkciu, ktora by mi z pointra na byte spravila bytove pole, musel som to prehnat for cyklami, ako vidis.
Napísať odpoveď