C# .net包装器适用于C DLL,特别是lglcd(g19 sdk)

6
Ok,大家好,这让我疯了。 我正在为C#创建一个C库的端口,但是在使用位图(由gdi生成)和字节数组(c lib所需)时遇到问题。
下面是代码(pastie),分为几个文件:
  1. Lglcd.dll: http://pastie.org/1424596 (已编译)
  2. G19dotNet.dll: http://pastie.org/1424600 (已编译, 这是C#的Interop库)
  3. TestProject: http://pastie.org/1424603 (已编译, 但抛出异常)
问题出现在最后一个文件中(其他两个非常直观),第116行。
res = LgLcd.NativeMethods.lgLcdUpdateBitmap(openContext.device, ref bmp.hdr, LgLcd.NativeConstants.LGLCD_SYNC_UPDATE(LgLcd.NativeConstants.LGLCD_PRIORITY_NORMAL));

这会在访问托管内存时出现无效内存访问异常。

该函数的签名如下:

/// Return Type: DWORD->unsigned int
            ///device: int
            ///bitmap: lgLcdBitmapHeader*
            ///priority: DWORD->unsigned int
            [System.Runtime.InteropServices.DllImportAttribute("LgLcd", EntryPoint = "lgLcdUpdateBitmap")]
            public static extern uint lgLcdUpdateBitmap([System.Runtime.InteropServices.In] int device, [System.Runtime.InteropServices.In] ref lgLcdBitmapHeader bitmap, [System.Runtime.InteropServices.In] uint priority);

正如您所看到的,第二个参数是指向lgLcdBitmapHeader的指针,但我猜测(因为我看到了库的旧版本)这个指针被转换为一个lgLcdBitmapQVGAx32指针(它是一个不同大小的结构体)

我认为问题就在这里,但我无法解决这个问题,真的

这是结构体的签名:

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct lgLcdBitmapHeader
    {

        /// DWORD->unsigned int
        public uint Format;
    }

并且

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct lgLcdBitmap160x43x1
    {

        /// lgLcdBitmapHeader->Anonymous_5fa96ca7_9cc3_4b33_b5aa_ccff9833813a
        public lgLcdBitmapHeader hdr;

        /// BYTE[]
        //public fixed byte pixels[NativeConstants.LGLCD_BMP_WIDTH * NativeConstants.LGLCD_BMP_HEIGHT * NativeConstants.LGLCD_BMP_BPP];
        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = NativeConstants.LGLCD_BMP_WIDTH * NativeConstants.LGLCD_BMP_HEIGHT * NativeConstants.LGLCD_BMP_BPP)]
        public byte[] pixels;
        public static int SizeConst { get { return NativeConstants.LGLCD_BMP_WIDTH * NativeConstants.LGLCD_BMP_HEIGHT * NativeConstants.LGLCD_BMP_BPP; } }
    }

我希望有人能够帮助我,我在网上搜索了很多,找到了这个库的 .net 版本,但它非常古老,并且没有我遇到的问题,因为它不使用彩色位图(每种颜色都有 4 个字节),也不使用 lgLcdBitmapHeader 结构体(它使用一个更简单的结构体)。此外,它的源代码与我的非常相似。
任何帮助将不胜感激。
有用的链接:

http://lglcdnet.codeplex.com/SourceControl/changeset/changes/5538

更新 1:

我基于一种理论取得了一些进展。

DWORD WINAPI lgLcdUpdateBitmap(IN int device,
                           IN const lgLcdBitmapHeader *bitmap,
                           IN DWORD priority);

这个签名在C语言中有着“特殊的含义”,因为结构体的第一个元素的指针也是该结构体的指针。 事实上,lgLcdBitmapQVGAx32的第一个元素的类型是lgLcdBitmapHeader。 也就是说,他们利用C语言将所有东西转换为任何东西的可能性来创建一个“通用方法”,因为lgLcdBitmapHeader既可以是lgLcdBitmap160x43x1(第一个元素是lgLcdBitmapHeader),也可以是lgLcdBitmapQVGAx32。
这是一个问题,因为在C#中我无法模拟这种能力,所以我创建了一些“辅助”函数,它们接受lgLcdBitmap160x43x1和lgLcdBitmapQVGAx32,并在内部将它们用作指向lgLcdBitmapHeader的指针。
然而,这样做却给我带来了另一个错误:
System.Runtime.InteropServices.MarshalDirectiveException non è stata gestita
  Message=Impossibile effettuare il marshalling di 'parameter #2': Limitazione interna: la struttura è troppo complessa o troppo grande.
  Source=G19dotNet
  StackTrace:
       in G19dotNet.LgLcd.NativeMethods.lgLcdUpdateBitmapQVGAx32(Int32 device, lgLcdBitmapQVGAx32& bitmap, UInt32 priority)
       in ConsoleTest2.Program.Main(String[] args) in C:\Documents and Settings\Administrator\documenti\visual studio 2010\Projects\G19dotNet\ConsoleTest2\Program.cs:riga 116
       in System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       in System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       in Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       in System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       in System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       in System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       in System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

System.Runtime.InteropServices.MarshalDirectiveException未处理 Message=无法对“参数#2”进行封送:内部限制:结构太复杂或太大。

它有一个307200字节的数组,我该怎么办?

更新2:

我设法在屏幕上显示图像,这意味着我的理论是正确的,我必须使用这种类型的“东西”才能使其工作:http://bytes.com/topic/c-sharp/answers/272048-internal-limitation-structure-too-complex-too-large 但是所显示的图像是“损坏的”,我的意思是它具有原始图像的形状,但有点混乱和无色,也许是因为我传递位图的方式?


也许在传递之前,您需要将某些内容转换为非托管类型? - Machinarius
毫无疑问,但是我不知道如何以这种方式编排,我不擅长Interop服务,我快疯了。 - Francesco Belladonna
我不是很确定,但你能否看一下http://msdn.microsoft.com/en-us/library/23acw07k.aspx? - P.K
2个回答

1

需要使用未托管的分配,正如问题中直接解释的那样。

代码示例:

IntPtr unhandledPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LgLcd.lgLcdBitmapQVGAx32)));
Marshal.StructureToPtr(bmp, unhandledPtr, true);
res = LgLcd.NativeMethods.lgLcdUpdateBitmap(openContext.device, unhandledPtr, LgLcd.NativeConstants.LGLCD_SYNC_UPDATE(LgLcd.NativeConstants.LGLCD_PRIORITY_NORMAL));
Marshal.FreeHGlobal(unhandledPtr);

我还需要更改外部方法的签名:

    [DllImportAttribute("LgLcd", EntryPoint = "lgLcdUpdateBitmap")]
    public static extern uint lgLcdUpdateBitmap([In] int device, [In] IntPtr bitmap, [In] uint priority);
    //OLD ONE:
    public static extern uint lgLcdUpdateBitmap([In] int device, [In] ref lgLcdBitmapHeader bitmap, [In] uint priority);

0

既然您有C DLL的源代码,建议使用Visual C++ CLR项目编写包装器。

公共语言运行时允许您同时编写托管和非托管代码。这样,您就不需要传递变量,而是直接使用它们。

当您想要从C#中使用它时,只需从C#应用程序引用C++ Clr库即可。我以前就是用这种方式,效果非常好。


我认为这样做可能更好,但从未使用过C++ CLR。 不过我已经成功地展示了一些东西,现在的问题是正确地显示位图。 - Francesco Belladonna
@Fire:好的,那么我想你应该检查一下像素格式。我们有pf 32bppARGB、pf 32bppRGBA...你是否可能看到了绿色的alpha值? - honibis
嗯,我设法用不同的方式让它工作了,我使用了PInvoke GetBitmapBits,它可以很好地与HBITMAP配合使用,我可以轻松地从我的位图中获取它...所以一切都很好,我认为这样也可以。顺便说一下,格式是RGBA。 - Francesco Belladonna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接