如何在 VB.NET/C# 中成功地使用 cpp dll 中的结构和嵌套结构?

How do I successfully work with structures and nested structures within a cpp dll in VB.NET/C#?

我想调用一个需要结构的 DLL 函数,而在该结构中是另一个结构。 Dll 应该 return 值到我的结构,但除了错误代码,我什么也没得到。有一次(以前编码)我在我的函数调用中成功 returned "True",但是我的结构中没有值。

我对封送处理等不是很熟悉,但如果有人可以请给我提供一个如何执行此操作的示例!

Dll 代码如下所示:


bXaarScorpionGetPrintDataParametersUpdated(struct UpdatedPrintDataParameters &DataParams);

第一个结构:

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

这是第二个结构:

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

我的 VB.Net 声明如下所示:

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

第一个结构:

   <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

第二个结构

<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

您在 dllimport 上使用了 Out 属性。如果您想查看 DLL 对您的结构所做的任何更改,请不要这样做。

至于您的其他错误,如果我们不知道它们是什么,我们将无法帮助您。

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

您得到的答案没有帮助。您实际上确实需要 <Out> 属性,由于数组和布尔成员,结构不是 "blittable"。一百美元的话,这意味着 pinvoke 编组器不能只将指针传递给托管变量,因为非托管布局与托管布局不同。默认情况下,pinvoke 编组器不会复制回结构,需要 OutAttribute 来改变它的想法。

您是否也需要 <[In]> 属性并不明显。可能不会,但是包含它不会有太大的伤害,反正这段代码不会很快。

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

这是一个怪物结构,您的 VB.NET 声明与 C 声明完全不匹配。它的大小甚至远不接近 66 字节。不仅大小必须匹配,字段在 C++ 和 VB.NET 代码中也需要具有相同的偏移量。如果存在不匹配,则 C 代码可能会随机失败并出现 AccessViolationException,并且编组器注定要将数据复制到完全错误的字段中。我将描述一个通用的 trouble-shooting 查找错误的策略。

您需要编写一个小的 C++ 程序,用 sizeof 运算符测量结构的大小。它的值需要 Marshal.SizeOf() 的 return 值完全 匹配。如果不匹配,则永远无法正确编组。您通过使用 C++ 中的 offsetof 宏和 VB.NET.

中的 Marshal.OffsetOf() 方法找到了错误的声明

开始的一些示例代码。 C++代码优先:

#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
}

和等效的 VB.NET 代码:

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

运行此代码as-is,C++程序将报告长度为312字节,SubPixelOffset字段的偏移量为140。VB.NET程序报告339和132。差别很大,多次出错,永远无法正常工作。

从 SubPixelOffset 字段向后查找第一个 trouble-maker。修复声明,使 ofs 值匹配,然后继续查找下一个错误。

至少 Boolean 成员是错误的,它们默认编组为 32 位整数,但 bool 在 C++ 程序中采用单个字节。它们需要 Byte 或需要 <MarshalAs(UnmanagedType.U1)> 属性。