How do I update EXIF Metadata?

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • DGrund
    New Member
    • Jan 2017
    • 23

    How do I update EXIF Metadata?

    I am looking for an example of a program, routine, or code snippet, that will show me exactly how to update a picture's exit metadata. I have been successful in READING this information (using the ExifWorks class).


    Thanks in advance!
    Last edited by Frinavale; Jan 11 '17, 02:37 PM.
  • Frinavale
    Recognized Expert Expert
    • Oct 2006
    • 9749

    #2
    I looked into this for you and I can't believe how messy it is.

    The way I figured out how to do this was to open an image (using the Image class), access the metadata on the file (represented through PropertyItem classes), edit the information and save it as a new image.

    Sounds simple but there is a lot to understand before this can be done.

    First of all the PropertyInfo class does contain metadata information BUT the information it gives you is pretty useless on its own without doing some research into it.

    The PropertyInfoclass has the following properties:
    • ID: Gets or sets the ID of the property and indicates what the metadata is (this is an integer value that is unintelligible without looking up what it represents)
    • Value: Gets or sets the value of the metadata (byte data which is unreadable and unusable until converted)
    • Type: Gets or sets an integer that defines the type of data contained in the Value property (since this is an integer, it is unintelligible without looking up what the integer represents)
    • Len: Gets or sets the length (in bytes) of the Value property.


    The first problem I found was that the ID property, which represents what the metadata is, is stored as a decimal-based integer value in the object; however, the documentation (found here and in other locations on the web) lists them in HEX!

    So what I did was copy the documentation into code as a HashTable, edit a .jpeg file and provide as much metadata as I could, loop through the PropertyItems for the image and for every known hex value I wrote it's corresponding Decimal value into a text document to generate an Enum that I could use in my code to do stuff.

    After setting as much metadata as I could through Windows Explorer I saw many hex ID values for metadata that I had no way of matching to meaningful data because the value is a byte and the type could not be converted to see what the values were (for example if the metadata had dates in it...those dates were made available as "bytes" or "byte arrays" but I had no idea which metadata had date values because type ID is "byte" or something I couldn't convert. If I had more time I would set each field one by one and see what the corresponding ID is and then "know" that it has a Date type in it...but this is just TOO much work for an example)

    The second problem is that the Value property is an array of bytes that you have to convert into an appropriate, corresponding type in your code so that you can actually see what that value is. You can find a table with the integer "Type" values matched with the corresponding data types in this article. And when you go to set the metadata information you need to convert it into a Byte array also.

    Lastly, the classes are designed by Microsoft in such a way that lets you edit existing metadata already part of the file; however it was not designed to let you create a new piece of metadata because the PropertyInfo class does not have a public constructor!!! I found a way around this using Reflection though, so my example demonstrates how to add a new piece of meta data (your welcome)

    That is pretty much the background (check out the links I have posed throughout my response for more information on everything).

    Here is my code example!

    Prerequisites:
    • I made sure that I had a .jpg file called ImageFile.jpg in my "My Pictures" folder. This image does not contain preset metadata for the "Title" metadata.
    • I used visual studio to create a Console type application.
    • This is my code within the Module1.vb file within my console application.


    Code:
    Imports System.Drawing.Imaging
    Imports System.Drawing
    Imports System.Text
    
    Module Module1
     Public Enum DecIDValues
            Title = 270 'Also Subject
            CameraMaker = 271
            CameraModel = 272
            Authors = 315
            Copyright = 33432
            ExifExposureProg = 34850
            ExifISOSpeed = 34855
            ExifDTOrig = 36867
            ExifDTDigitized = 36868
            ExifMeteringMode = 37383
            ExifLightSource = 37384
            ExifFlash = 37385
            ExifDTOrigSS = 37521
            ExifDTDigSS = 37522
            ThumbnailCompression = 20515
            ThumbnailResolutionUnit = 20528
            ExifUserComment = 37510
            ChrominanceTable = 20625
            LuminanceTable = 20624
        End Enum
    
        Private typeValuesTable As New Hashtable
        Private idValuesTable As New Hashtable
        Private ascii As Encoding = Encoding.ASCII
    
        Sub Main()
            SetUpHashTables()
            SetMetaData()
            DisplayImageMetaData()
            Console.ReadLine()
        End Sub
    
        Private Sub SetMetaData()
            'you need to change the path to the file you are going to edit
            Dim newImage As Bitmap = New Bitmap("C:\Users\Frinavale\Pictures\Saved\ImageFile.jpg")
    
             'newItem is constructed as a PropertyItem class that will be used to set the Title metadata for the image fiel
            Dim newItem = DirectCast(Runtime.Serialization.FormatterServices.GetUninitializedObject(GetType(PropertyItem)), PropertyItem)
            newItem.Id = DecIDValues.Title 'See the Enum value
            newItem.Value = Encoding.ASCII.GetBytes("Title set from Module 1")' Using the ASCII encoder to set the metadata to a byte array with the string data we want
            newItem.Len = newItem.Len 'Making sure the length is set because otherwise it won't show up in the metadata when viewing it later
            newItem.Type = 2 'Setting the type to indicate it is ASCII
            newImage.SetPropertyItem(newItem) 'Using the SetPropertyItem method to set the metadata information
    
            'The following commented out code will loop through all of the 
            'Existing meta data and if it is an ASCII type it changes it
            'It is to demonstrate how to "edit" an existing value rather than add a new one
    
            'For Each piNewImage In newImage.PropertyItems
            '    If piNewImage.Type = 2 Then
            '        piNewImage.Value = Encoding.ASCII.GetBytes("Changed by module1")
            '        newImage.RemovePropertyItem(piNewImage.Id)
            '        newImage.SetPropertyItem(piNewImage)
            '    End If
            'Next
    
    
            newImage.Save("C:\Users\Frinavale\Pictures\Saved\ImageFile2.jpg")
            newImage.Dispose()
    
            'At this point you can consider deleting the original
    
        End Sub
        Private Sub DisplayImageMetaData()
            'Opening the image
            Dim theImage As Bitmap = New Bitmap("C:\Users\Frinavale\Pictures\Saved\ImageFile2.jpg") 
            'Grabbing the metadata
            Dim propItems As PropertyItem() = theImage.PropertyItems
    
            'Looping through each piece of metadata and displaying its information (if possible)
            For Each pi In propItems
                Dim id = String.Format("0x{0}", pi.Id.ToString("x")) 'Retrieving the name of the metadata (if it is known) from the hashtable with the IDs and names
                Console.WriteLine("ID: {0}", If(idValuesTable(id) Is Nothing, id, idValuesTable(id))) ' Displaying the name of the metadata, or it's HEX value
                Console.WriteLine("Dec ID: {0}", pi.Id.ToString) 'Displaying the Decimal value for the ID
                Dim type As String = pi.Type.ToString 
                Console.WriteLine("Len: {0}", pi.Len.ToString)
                Console.WriteLine("Type: {0}", If(typeValuesTable(type) Is Nothing, type, typeValuesTable(type))) 'Displaying the "type" of metadata 
                Console.Write("Value: ")
    
               'The following attempts to display the value set for the metadata
               'If there is no corresponding type in VB.NET, or if it is impossible to display...it just writes the type it is supposed to be.
                Select Case pi.Type
                    Case 1 'byte
                        Console.Write("...byte...")
                    Case 2 'array of byte objects encoded as ASCII
    
                        Dim asciiChars(ascii.GetCharCount(pi.Value, 0, pi.Value.Length) - 1) As Char
                        ascii.GetChars(pi.Value, 0, pi.Value.Length, asciiChars, 0)
                        Dim asciiString As New String(asciiChars)
                        Console.Write(asciiString)
                    Case 3 ' 16 bit int
                        Dim i As Int16 = BitConverter.ToInt16(pi.Value, 0)
                        Console.Write(i.ToString)
                    Case 4 ' 32 bit int
                        Dim i As Int32 = BitConverter.ToInt32(pi.Value, 0)
                        Console.Write(i.ToString)
                    Case 5 ' array of 2 byte objects: rational number
                        Console.Write("...A Rational number (from 2 bytes)...")
                    Case 6 ' Not used
                        Console.Write("...not used...")
                    Case 7 ' undefined
                        Console.Write("...undefined...")
                    Case 8 'Not used
                        Console.Write("...not used...")
                    Case 9 'SLong
                        Dim i As Long = BitConverter.ToDouble(pi.Value, 0)
                        Console.Write(i.ToString)
                    Case 10 'SRational
                        Console.Write("...SRational number...")
                End Select
                Console.WriteLine()
                Console.WriteLine()
            Next
    
            theImage.Dispose()
        End Sub
    
        Private Sub SetUpHashTables()
            typeValuesTable.Clear()
            typeValuesTable.Add("1", "A Byte")
            typeValuesTable.Add("2", "An array of Byte objects encoded as ASCII")
            typeValuesTable.Add("3", "A 16-bit integer")
            typeValuesTable.Add("4", "A 32-bit integer")
            typeValuesTable.Add("5", "An array of two Byte objects that represent a rational number")
            typeValuesTable.Add("6", "Not used")
            typeValuesTable.Add("7", "Undefined")
            typeValuesTable.Add("8", "Not used")
            typeValuesTable.Add("9", "SLong")
            typeValuesTable.Add("10", "SRational")
    
    
            idValuesTable.Clear()
            idValuesTable.Add("x0000", "GpsVer")
            idValuesTable.Add("0x0001", "GpsLatitudeRef")
            idValuesTable.Add("0x0002", "GpsLatitude")
            idValuesTable.Add("0x0003", "GpsLongitudeRef")
            idValuesTable.Add("0x0004", "GpsLongitude")
            idValuesTable.Add("0x0005", "GpsAltitudeRef")
            idValuesTable.Add("0x0006", "GpsAltitude")
            idValuesTable.Add("0x0007", "GpsGpsTime")
            idValuesTable.Add("0x0008", "GpsGpsSatellites")
            idValuesTable.Add("0x0009", "GpsGpsStatus")
            idValuesTable.Add("0x000A", "GpsGpsMeasureMode")
            idValuesTable.Add("0x000B", "GpsGpsDop")
            idValuesTable.Add("0x000C", "GpsSpeedRef")
            idValuesTable.Add("0x000D", "GpsSpeed")
            idValuesTable.Add("0x000E", "GpsTrackRef")
            idValuesTable.Add("0x000F", "GpsTrack")
            idValuesTable.Add("0x0010", "GpsImgDirRef")
            idValuesTable.Add("0x0011", "GpsImgDir")
            idValuesTable.Add("0x0012", "GpsMapDatum")
            idValuesTable.Add("0x0013", "GpsDestLatRef")
            idValuesTable.Add("0x0014", "GpsDestLat")
            idValuesTable.Add("0x0015", "GpsDestLongRef")
            idValuesTable.Add("0x0016", "GpsDestLong")
            idValuesTable.Add("0x0017", "GpsDestBearRef")
            idValuesTable.Add("0x0018", "GpsDestBear")
            idValuesTable.Add("0x0019", "GpsDestDistRef")
            idValuesTable.Add("0x001A", "GpsDestDist")
            idValuesTable.Add("0x00FE", "NewSubfileType")
            idValuesTable.Add("0x00FF", "SubfileType")
            idValuesTable.Add("0x0100", "ImageWidth")
            idValuesTable.Add("0x0101", "ImageHeight")
            idValuesTable.Add("0x0102", "BitsPerSample")
            idValuesTable.Add("0x0103", "Compression")
            idValuesTable.Add("0x0106", "PhotometricInterp")
            idValuesTable.Add("0x0107", "ThreshHolding")
            idValuesTable.Add("0x0108", "CellWidth")
            idValuesTable.Add("0x0109", "CellHeight")
            idValuesTable.Add("0x010A", "FillOrder")
            idValuesTable.Add("0x010D", "DocumentName")
            idValuesTable.Add("0x010E", "ImageDescription")
            idValuesTable.Add("0x010F", "EquipMake")
            idValuesTable.Add("0x0110", "EquipModel")
            idValuesTable.Add("0x0111", "StripOffsets")
            idValuesTable.Add("0x0112", "Orientation")
            idValuesTable.Add("0x0115", "SamplesPerPixel")
            idValuesTable.Add("0x0116", "RowsPerStrip")
            idValuesTable.Add("0x0117", "StripBytesCount")
            idValuesTable.Add("0x0118", "MinSampleValue")
            idValuesTable.Add("0x0119", "MaSampleValue")
            idValuesTable.Add("0x011A", "Resolution")
            idValuesTable.Add("0x011B", "YResolution")
            idValuesTable.Add("0x011C", "PlanarConfig")
            idValuesTable.Add("0x011D", "PageName")
            idValuesTable.Add("0x011E", "XPosition")
            idValuesTable.Add("0x011F", "YPosition")
            idValuesTable.Add("0x0120", "FreeOffset")
            idValuesTable.Add("0x0121", "FreeByteCounts")
            idValuesTable.Add("0x0122", "GrayResponseUnit")
            idValuesTable.Add("0x0123", "GrayResponseCurve")
            idValuesTable.Add("0x0124", "T4Option")
            idValuesTable.Add("0x0125", "T6Option")
            idValuesTable.Add("0x0128", "ResolutionUnit")
            idValuesTable.Add("0x0129", "PageNumber")
            idValuesTable.Add("0x012D", "TransferFunction")
            idValuesTable.Add("0x0131", "SoftwareUsed")
            idValuesTable.Add("0x0132", "DateTime")
            idValuesTable.Add("0x013B", "Artist")
            idValuesTable.Add("0x013C", "HostComputer")
            idValuesTable.Add("0x013D", "Predictor")
            idValuesTable.Add("0x013E", "WhitePoint")
            idValuesTable.Add("0x013F", "PrimaryChromaticities")
            idValuesTable.Add("0x0140", "ColorMap")
            idValuesTable.Add("0x0141", "HalftoneHints")
            idValuesTable.Add("0x0142", "TileWidth")
            idValuesTable.Add("0x0143", "TileLength")
            idValuesTable.Add("0x0144", "TileOffset")
            idValuesTable.Add("0x0145", "TileByteCounts")
            idValuesTable.Add("0x014C", "InkSet")
            idValuesTable.Add("0x014D", "InkNames")
            idValuesTable.Add("0x014E", "NumberOfInks")
            idValuesTable.Add("0x0150", "DotRange")
            idValuesTable.Add("0x0151", "TargetPrinter")
            idValuesTable.Add("0x0152", "ExtraSamples")
            idValuesTable.Add("0x0153", "SampleFormat")
            idValuesTable.Add("0x0154", "SMinSampleValue")
            idValuesTable.Add("0x0155", "SMaxSampleValue")
            idValuesTable.Add("0x0156", "TransferRange")
            idValuesTable.Add("0x0200", "JPEGProc")
            idValuesTable.Add("0x0201", "JPEGInterFormat")
            idValuesTable.Add("0x0202", "JPEGInterLength")
            idValuesTable.Add("0x0203", "JPEGRestartInterval")
            idValuesTable.Add("0x0205", "JPEGLosslessPredictors")
            idValuesTable.Add("0x0206", "JPEGPointTransforms")
            idValuesTable.Add("0x0207", "JPEGQTables")
            idValuesTable.Add("0x0208", "JPEGDCTables")
            idValuesTable.Add("0x0209", "JPEGACTables")
            idValuesTable.Add("0x0211", "YCbCrCoefficients")
            idValuesTable.Add("0x0212", "YCbCrSubsampling")
            idValuesTable.Add("0x0213", "YCbCrPositioning")
            idValuesTable.Add("0x0214", "REFBlackWhite")
            idValuesTable.Add("0x0301", "Gamma")
            idValuesTable.Add("0x0302", "ICCProfileDescriptor")
            idValuesTable.Add("0x0303", "SRGBRenderingIntent")
            idValuesTable.Add("0x0320", "ImageTitle")
            idValuesTable.Add("0x5001", "ResolutionXUnit")
            idValuesTable.Add("0x5002", "ResolutionYUnit")
            idValuesTable.Add("0x5003", "ResolutionXLengthUnit")
            idValuesTable.Add("0x5004", "ResolutionYLengthUnit")
            idValuesTable.Add("0x5005", "PrintFlags")
            idValuesTable.Add("0x5006", "PrintFlagsVersion")
            idValuesTable.Add("0x5007", "PrintFlagsCrop")
            idValuesTable.Add("0x5008", "PrintFlagsBleedWidth")
            idValuesTable.Add("0x5009", "PrintFlagsBleedWidthScale")
            idValuesTable.Add("0x500A", "HalftoneLPI")
            idValuesTable.Add("0x500B", "HalftoneLPIUnit")
            idValuesTable.Add("0x500C", "HalftoneDegree")
            idValuesTable.Add("0x500D", "HalftoneShape")
            idValuesTable.Add("0x500E", "HalftoneMisc")
            idValuesTable.Add("0x500F", "HalftoneScreen")
            idValuesTable.Add("0x5010", "JPEGQuality")
            idValuesTable.Add("0x5011", "GridSize")
            idValuesTable.Add("0x5012", "ThumbnailFormat")
            idValuesTable.Add("0x5013", "ThumbnailWidth")
            idValuesTable.Add("0x5014", "ThumbnailHeight")
            idValuesTable.Add("0x5015", "ThumbnailColorDepth")
            idValuesTable.Add("0x5016", "ThumbnailPlanes")
            idValuesTable.Add("0x5017", "ThumbnailRawBytes")
            idValuesTable.Add("0x5018", "ThumbnailSize")
            idValuesTable.Add("0x5019", "ThumbnailCompressedSize")
            idValuesTable.Add("0x501A", "ColorTransferFunction")
            idValuesTable.Add("0x501B", "ThumbnailData")
            idValuesTable.Add("0x5020", "ThumbnailImageWidth")
            idValuesTable.Add("0x5021", "ThumbnailImageHeight")
            idValuesTable.Add("0x5022", "ThumbnailBitsPerSample")
            idValuesTable.Add("0x5023", "ThumbnailCompression")
            idValuesTable.Add("0x5024", "ThumbnailPhotometricInterp")
            idValuesTable.Add("0x5025", "ThumbnailImageDescription")
            idValuesTable.Add("0x5026", "ThumbnailEquipMake")
            idValuesTable.Add("0x5027", "ThumbnailEquipModel")
            idValuesTable.Add("0x5028", "ThumbnailStripOffsets")
            idValuesTable.Add("0x5029", "ThumbnailOrientation")
            idValuesTable.Add("0x502A", "ThumbnailSamplesPerPixel")
            idValuesTable.Add("0x502B", "ThumbnailRowsPerStrip")
            idValuesTable.Add("0x502C", "ThumbnailStripBytesCount")
            idValuesTable.Add("0x502D", "ThumbnailResolutionX")
            idValuesTable.Add("0x502E", "ThumbnailResolutionY")
            idValuesTable.Add("0x502F", "ThumbnailPlanarConfig")
            idValuesTable.Add("0x5030", "ThumbnailResolutionUnit")
            idValuesTable.Add("0x5031", "ThumbnailTransferFunction")
            idValuesTable.Add("0x5032", "ThumbnailSoftwareUsed")
            idValuesTable.Add("0x5033", "ThumbnailDateTime")
            idValuesTable.Add("0x5034", "ThumbnailArtist")
            idValuesTable.Add("0x5035", "ThumbnailWhitePoint")
            idValuesTable.Add("0x5036", "ThumbnailPrimaryChromaticities")
            idValuesTable.Add("0x5037", "ThumbnailYCbCrCoefficients")
            idValuesTable.Add("0x5038", "ThumbnailYCbCrSubsampling")
            idValuesTable.Add("0x5039", "ThumbnailYCbCrPositioning")
            idValuesTable.Add("0x503A", "ThumbnailRefBlackWhite")
            idValuesTable.Add("0x503B", "ThumbnailCopyRight")
            idValuesTable.Add("0x5090", "LuminanceTable")
            idValuesTable.Add("0x5091", "ChrominanceTable")
            idValuesTable.Add("0x5100", "FrameDelay")
            idValuesTable.Add("0x5101", "LoopCount")
            idValuesTable.Add("0x5102", "GlobalPalette")
            idValuesTable.Add("0x5103", "IndexBackground")
            idValuesTable.Add("0x5104", "IndexTransparent")
            idValuesTable.Add("0x5110", "PixelUnit")
            idValuesTable.Add("0x5111", "PixelPerUnitX")
            idValuesTable.Add("0x5112", "PixelPerUnitY")
            idValuesTable.Add("0x5113", "PaletteHistogram")
            idValuesTable.Add("0x8298", "Copyright")
            idValuesTable.Add("0x829A", "ExifExposureTime")
            idValuesTable.Add("0x829D", "ExifFNumber")
            idValuesTable.Add("0x8769", "ExifIFD")
            idValuesTable.Add("0x8773", "ICCProfile")
            idValuesTable.Add("0x8822", "ExifExposureProg")
            idValuesTable.Add("0x8824", "ExifSpectralSense")
            idValuesTable.Add("0x8825", "GpsIFD")
            idValuesTable.Add("0x8827", "ExifISOSpeed")
            idValuesTable.Add("0x8828", "ExifOECF")
            idValuesTable.Add("0x9000", "ExifVer")
            idValuesTable.Add("0x9003", "ExifDTOrig")
            idValuesTable.Add("0x9004", "ExifDTDigitized")
            idValuesTable.Add("0x9101", "ExifCompConfig")
            idValuesTable.Add("0x9102", "ExifCompBPP")
            idValuesTable.Add("0x9201", "ExifShutterSpeed")
            idValuesTable.Add("0x9202", "ExifAperture")
            idValuesTable.Add("0x9203", "ExifBrightness")
            idValuesTable.Add("0x9204", "ExifExposureBias")
            idValuesTable.Add("0x9205", "ExifMaxAperture")
            idValuesTable.Add("0x9206", "ExifSubjectDist")
            idValuesTable.Add("0x9207", "ExifMeteringMode")
            idValuesTable.Add("0x9208", "ExifLightSource")
            idValuesTable.Add("0x9209", "ExifFlash")
            idValuesTable.Add("0x920A", "ExifFocalLength")
            idValuesTable.Add("0x927C", "ExifMakerNote")
            idValuesTable.Add("0x9286", "ExifUserComment")
            idValuesTable.Add("0x9290", "ExifDTSubsec")
            idValuesTable.Add("0x9291", "ExifDTOrigSS")
            idValuesTable.Add("0x9292", "ExifDTDigSS")
            idValuesTable.Add("0xA000", "ExifFPXVer")
            idValuesTable.Add("0xA001", "ExifColorSpace")
            idValuesTable.Add("0xA002", "ExifPixXDim")
            idValuesTable.Add("0xA003", "ExifPixYDim")
            idValuesTable.Add("0xA004", "ExifRelatedWav")
            idValuesTable.Add("0xA005", "ExifInterop")
            idValuesTable.Add("0xA20B", "ExifFlashEnergy")
            idValuesTable.Add("0xA20C", "ExifSpatialFR")
            idValuesTable.Add("0xA20E", "ExifFocalXRes")
            idValuesTable.Add("0xA20F", "ExifFocalYRes")
            idValuesTable.Add("0xA210", "ExifFocalResUnit")
            idValuesTable.Add("0xA214", "ExifSubjectLoc")
            idValuesTable.Add("0xA215", "ExifExposureIndex")
            idValuesTable.Add("0xA217", "ExifSensingMethod")
            idValuesTable.Add("0xA300", "ExifFileSource")
            idValuesTable.Add("0xA301", "ExifSceneType")
            idValuesTable.Add("0xA302", "ExifCfaPattern")
    
    
            idValuesTable.Add("0x10e", "Title")
            idValuesTable.Add("0x10f", "CameraMaker")
            idValuesTable.Add("0x110", "CameraModel")
            idValuesTable.Add("0x13b", "Authors")
    
        End Sub
    
    End Module
    Last edited by Frinavale; Jan 11 '17, 07:18 PM.

    Comment

    • DGrund
      New Member
      • Jan 2017
      • 23

      #3
      Wow! Thank you so much for all of your time and effort. I will grab this code and apply it, and see if I can get a finished product. I will be happy to share it with you once I am done.

      Comment

      • Frinavale
        Recognized Expert Expert
        • Oct 2006
        • 9749

        #4
        Oh yeah, I forgot to mention that not all metadata tags are available for all image types. Make sure the metadata that you are trying to set can actually be set for the image type you are working with (tiff, png, jpg, etc.)


        Let me know if you have a problem and I'll try to help.

        I'd be interested if you find a better solution too...

        I mean, this seems unnecessarily complication, convoluted and cryptic. Why is it so hard? Seriously it's metadata!
        Last edited by Frinavale; Jan 11 '17, 09:59 PM.

        Comment

        • Upsilon648
          New Member
          • Dec 2023
          • 1

          #5
          Thank you @frinavale. This code is working for me. I did have to use the .NET Frameworks, as the BITMAP class is not found in the .NET core.

          Comment

          Working...