How do I successfully work with structures and nes

2019-07-24 13:00发布

问题:

I want to call a DLL function that wants a structure, and within that structure is another structure. The Dll should return values to my structures but I get nothing but error codes. One time (some coding back ago) I successfully returned "True" on my function call, but there were no values in my structures.

I am not very familiar with marshaling and such, but if someone could please provide me an example on how to do this!

The Dll code looks like this:


bXaarScorpionGetPrintDataParametersUpdated(struct UpdatedPrintDataParameters &DataParams);

First struct:

struct UpdatedPrintDataParameters
{
struct PrintDataParameters OriginalParameters;
DWORD RowTrailChannels[MAXROWS];        // The number of unused channels at end of print head per row
DWORD RowLeadChannels[MAXROWS];         // The number of unused channels at start of print head per row
DWORD CopyCount[MAXROWS];               // The number of repeated copies
DWORD XPMSEPDSetup[4];                  // XPM - bits to control shaft encoder and product detect configuration 
DWORD PDFilter;                         // XPM - Product Detect Filter, pass value through
DWORD Spare[13];                        // Spare

And here is the second struct:

struct PrintDataParameters
{
// Head Setup parameters
DWORD Head;                             // This printhead number
DWORD HeadType;                         // Code indicating type of printhead connected
DWORD HeadIndex[MAXROWS];               // The index of the printhead in the array of actually connected printhead
DWORD NumberOfRows;                     // The number of rows on the printhead
DWORD SeparateRows;                     // If true, treat each row as an individual head
DWORD ImageLength[MAXROWS];             // The number of strokes in an image
DWORD ImageSize[MAXROWS];               // The number of bytes in an image
DWORD ProductOffset;                    // Number of strokes of Offset after the product and before the print starts
DWORD InterGap;                         // Gap used between continuous prints
DWORD FirstSwatheBlock;                 // The memory address where 1st swathe control block is stored
DWORD SwatheBlock;                      // The memory address to store this particular swathe control block
DWORD ThisSwathe;                       // The number of the active swathe
DWORD NextSwatheBlock;                  // The memory address where the next swathe block will be stored
DWORD MemoryBlock[MAXROWS];             // The memory address to store this image block
DWORD FirstMemoryBlock[MAXROWS];        // The memory address where 1st image swathe is stored
DWORD MemoryBlocksNeeded[MAXROWS];      // The number of memory blocks needed to store the image swathe
DWORD PreLoadSwatheBlock;               // The number of memory blocks that the pre-load strokes requires
DWORD PrintMode;                        // The print mode e.g. single shot etc.
bool  PrintOnce;                        // If true only one complete print is required
DWORD CycleMode;                        // Cycle Mode (e.g. set to PIXELMODE, CYCLEMODE)
bool ForwardBuffer;                     // Print direction i.e. forward or reverse
DWORD StartDir[MAXROWS];                // The starting head direction bit for each row 
DWORD DirBlock;                         // The direction to use for this swathe

// System setup parameters
DWORD SubPixelDivide;                   // The subpixel divide value
DWORD SaveSubPixelOffset[2][MAXROWS];   // The subpixeloffsets to use, 1st index is for forward or reverse offsets, 2nd index = row
DWORD SubPixelOffset;                   // The sub pixel offset to use for this swathe
DWORD EncoderDivide;                    // A copy of the encoder divide

// Image control parameters
DWORD TrailChannels;                    // The number of unused channels at end of print head - same value currently used for both rows, max 31
DWORD LeadChannels;                     // The number of unused channels at start of print head - same value currently used for both rows, max 31
DWORD DataChannels;                     // The total number of printing channels
DWORD HeadChannels;                     // The number of printing channels per side
bool BufferReverse[MAXROWS];            // The direction to read the data from the image buffer eg for 760, [0] = true, [1] = false
DWORD NibbleControl[MAXROWS];           // For each row defines if the even/odd/both nibbles of image data is used for printing
DWORD NibbleIndex;                      // Used to defines if we are using row 1 or row 2
DWORD LoopCount;                        // Set this to 1

LPSTR lpDIBBits;                        // Pointer to the bitmap in (screen) memory
DWORD TotalImageWidth[MAXROWS];         // The total width of the image
DWORD BitDifference;                    // The number of bits to store .... this needs to be set to 4

// Swathe control parameters
DWORD NumberSwathes[MAXROWS];           // The number of swathes to print entire image
DWORD SwatheMemoryCount[MAXROWS];       // The total number of swathes that will fit into memory for this head
DWORD StoredSwathes[MAXROWS];           // The total number of swathes that have been stored to the XUSB box
DWORD PreviousPrintSwathe[MAXROWS];     // The number of the previous swathe that was stored
bool AllSwathesFit[MAXROWS];            // True if all the swathes fit in memory at once
bool Binary;                            // True if binary or false if greyscale head?
DWORD GreyLevel;                        // The number of grey levels
bool FirstSwathe[MAXROWS];              // This should be set to true for each row for the 1st swathe of a print (doesn't need to be set again for repeat print swathes)
bool LastSwathe[MAXROWS];               // This should be set for last swathe - specifies if the image is only required to be printed once
bool LastSwatheInMemory[MAXROWS];       // This indicates that this swathe is at the end of the swathe memory
DWORD SendID[MAXROWS];                  // Id of the swathe that has been setup for sending to xusb box
bool BiPrintKeepOdd;                    // Defines if, when in bi-directional printing the number of swathes are rounded up

// These 2 values are used when a print head is only required to print part of an image
DWORD SwatheStartIndex;                 // The offset into the swathe to start printing from
DWORD SwatheIncrement;                  // The amount to add to locate the next swathe

DWORD SourceStrokeWidth;                // The number of blocks required for each image stroke

// print parameters
DWORD PrintTransportMode;               // Used to determine if bi-directional printing is required
bool bReverseSwatheOrder;               // Define if the 1st or last swathe should be printed first
bool bReverseImageOrder;                // Specify if the 1st or last stroke of the image is printed first
bool bPaletteRemap;                     // True if palette remap required
bool bBinaryBackgroundInvert;           // Invert the background for a binary image
DWORD SaveProductOffset[2][MAXROWS];    // 1st index if to forward or reverse offsets
bool bSelectHead[MAXROWS];              // This printhead is selected for print

DWORD GuardValue;                       // Set guard channels to this value

DWORD SEPDSetup;                        // Bits to control SE and PD configuration. Effectively the ID of the shaftencoder/product detect pair
bool Enable2Bit;                        // True if 2 bit mode is enabled
BYTE SysClock;                          // Encoder mode i.e. Internal, external or absolute
BYTE VLDPHCount;                        // The number of 16 nozzle VLDPH print units
BYTE Spare;                             // Spare

My VB.Net declarations look like this:

  <DllImport("ScorpionDLL.dll", CallingConvention:=CallingConvention.StdCall)>
Public Shared Function bXaarScorpionGetPrintDataParametersUpdated(ByRef UDataParams As UpdatedPrintDataParameters) As IntPtr
End Function

First struct:

   <System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Structure UpdatedPrintDataParameters

    '''PrintDataParameters
    Public OriginalParameters As PrintDataParameters

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public RowTrailChannels() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public RowLeadChannels() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public CopyCount() As UInteger

    '''DWORD[4]
    <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=4, ArraySubType:=System.Runtime.InteropServices.UnmanagedType.U4)>
    Public XPMSEPDSetup() As UInteger

    '''DWORD->unsigned int
    Public PDFilter As UInteger

    '''DWORD[13]
    <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=13, ArraySubType:=System.Runtime.InteropServices.UnmanagedType.U4)>
    Public Spare() As UInteger
    '7
End Structure

Second struct

<System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet:=CharSet.Auto, Size:=66)>
Public Structure PrintDataParameters
    '''DWORD->unsigned int
    Public Head As UInteger

    '''DWORD->unsigned int
    Public HeadType As UInteger

    '''DWORD[]
    <MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst:=2)> Public HeadIndex() As UInteger

    '''DWORD->unsigned int
    Public NumberOfRows As UInteger

    '''DWORD->unsigned int
    Public SeparateRows As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public ImageLength() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public ImageSize() As UInteger

    '''DWORD->unsigned int
    Public ProductOffset As UInteger

    '''DWORD->unsigned int
    Public InterGap As UInteger

    '''DWORD->unsigned int
    Public FirstSwatheBlock As UInteger

    '''DWORD->unsigned int
    Public SwatheBlock As UInteger

    '''DWORD->unsigned int
    Public ThisSwathe As UInteger

    '''DWORD->unsigned int
    Public NextSwatheBlock As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public MemoryBlock() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public FirstMemoryBlock() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public MemoryBlocksNeeded() As UInteger

    '''DWORD->unsigned int
    Public PreLoadSwatheBlock As UInteger

    '''DWORD->unsigned int
    Public PrintMode As UInteger

    '''boolean
    <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)>
    Public PrintOnce As Boolean

    '''DWORD->unsigned int
    Public CycleMode As UInteger

    '''boolean
    <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)>
    Public ForwardBuffer As Boolean

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public StartDir() As UInteger

    '''DWORD->unsigned int
    Public DirBlock As UInteger

    '''DWORD->unsigned int
    Public SubPixelDivide As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public SaveSubPixelOffset(,) As UInteger

    '''DWORD->unsigned int
    Public SubPixelOffset As UInteger

    '''DWORD->unsigned int
    Public EncoderDivide As UInteger

    '''DWORD->unsigned int
    Public TrailChannels As UInteger

    '''DWORD->unsigned int
    Public LeadChannels As UInteger

    '''DWORD->unsigned int
    Public DataChannels As UInteger

    '''DWORD->unsigned int
    Public HeadChannels As UInteger

    '''boolean[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public BufferReverse() As Boolean

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public NibbleControl() As UInteger

    '''DWORD->unsigned int
    Public NibbleIndex As UInteger

    '''DWORD->unsigned int
    Public LoopCount As UInteger

    '''LPSTR->CHAR*
    <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)>
    Public lpDIBBits As String

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public TotalImageWidth() As UInteger

    '''DWORD->unsigned int
    Public BitDifference As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public NumberSwathes() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public SwatheMemoryCount() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public StoredSwathes() As UInteger

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public PreviousPrintSwathe() As UInteger

    '''boolean[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public AllSwathesFit() As Boolean

    '''boolean
    Public Binary As Boolean

    '''DWORD->unsigned int
    Public GreyLevel As UInteger

    '''boolean[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public FirstSwathe() As Boolean

    '''boolean[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public LastSwathe() As Boolean

    '''boolean[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public LastSwatheInMemory() As Boolean

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public SendID() As UInteger

    '''boolean
    Public BiPrintKeepOdd As Boolean

    '''DWORD->unsigned int
    Public SwatheStartIndex As UInteger

    '''DWORD->unsigned int
    Public SwatheIncrement As UInteger

    '''DWORD->unsigned int
    Public SourceStrokeWidth As UInteger

    '''DWORD->unsigned int
    Public PrintTransportMode As UInteger

    '''boolean
    Public bReverseSwatheOrder As Boolean

    '''boolean
    Public bReverseImageOrder As Boolean

    '''boolean
    Public bPaletteRemap As Boolean

    '''boolean
    Public bBinaryBackgroundInvert As Boolean

    '''DWORD[]
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Public SaveProductOffset(,) As UInteger

    '''boolean[]
    Public bSelectHead() As Boolean

    '''DWORD->unsigned int
    Public GuardValue As UInteger

    '''DWORD->unsigned int
    Public SEPDSetup As UInteger

    '''boolean
    Public Enable2Bit As Boolean

    '''BYTE->unsigned char
    Public SysClock As Byte

    '''BYTE->unsigned char
    Public VLDPHCount As Byte

    '''BYTE->unsigned char
    Public Spare As Byte
    '66
End Structure

回答1:

... they are all "0"... What can be the cause of that?

The answer you got was not helpful. You do in fact need the <Out> attribute, the structure is not "blittable" because of the arrays and the Boolean members. A hundred dollar word that means that the pinvoke marshaller cannot just pass a pointer to the managed variable because the unmanaged layout is not the same as the managed layout. By default the pinvoke marshaller does not copy back a struct, the OutAttribute is required to change its mind.

Whether you need the <[In]> attribute as well is not obvious. Probably not, but it won't hurt that much to include it, this code is not going to be fast anyway.

<System.Runtime.InteropServices.StructLayoutAttribute(..., Size:=66)>

It is a monster struct and your VB.NET declaration does not match the C declaration at all. Its size is not even remotely close to 66 bytes. Not only the size must match, the fields need to be at the same offset both in the C++ and the VB.NET code. If there is a mismatch then the C code can randomly fail with an AccessViolationException and the marshaller is doomed to copy the data into the completely wrong fields. I'll describe a general trouble-shooting strategy to find the mistakes.

You need to write a small C++ program that measures the size of the struct with the sizeof operator. Its value needs to be an exact match with the return value of Marshal.SizeOf(). If it is not a match then it cannot ever marshal correctly. You find the wrong declaration(s) by using the offsetof macro in C++ and the Marshal.OffsetOf() method in VB.NET.

Some sample code to get that started. The C++ code first:

#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>
#define MAXROWS 2

struct PrintDataParameters
{
   // etc..
};

int main()
{
    auto len = sizeof(PrintDataParameters);
    auto ofs = offsetof(PrintDataParameters, SubPixelOffset);
    return 0;   // Set a breakpoint here, inspect len and ofs
}

And the equivalent VB.NET code:

Imports System.Runtime.InteropServices

Module Module1

    Sub Main()
        Dim len = Marshal.SizeOf(GetType(PrintDataParameters))
        Dim ofs = Marshal.OffsetOf(GetType(PrintDataParameters), "SubPixelOffset")
        Console.ReadLine()    '' Set breakpoint here, inspect len and ofs
End Sub

    <StructLayoutAttribute(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
    Public Structure PrintDataParameters
       '' etc...
    End Structure
End Module

Running this code as-is, the C++ program will report a length of 312 bytes and an offset for the SubPixelOffset field of 140. The VB.NET program reports 339 and 132. Big difference, with multiple mistakes, it can never work correctly.

Work backwards from the SubPixelOffset field to find the first trouble-maker. Fix the declaration so the ofs value matches, then work forwards to find the next mistake.

At least the Boolean members are wrong, they marshal by default as a 32-bit Integer but a bool takes a single byte in a C++ program. They need to be Byte or need the <MarshalAs(UnmanagedType.U1)> attribute.



回答2:

You used the Out attribute on your dllimport. Don't do that if you want to see whatever changes the DLL made to your structure.

As for your other errors, we can't help you with them if we don't know what they are.