从C#调用.DLL的奇怪问题

时间:2009-05-09 03:53:44

标签: c# pinvoke htmltidy

我正在尝试从C#调用HtmlTidy库dll。网上有几个漂浮的例子,但没有任何确定性......而且我没有遇到麻烦。我很确定问题在于p / invoke声明......但如果我知道我哪里出错了就会有问题。

我从http://www.paehl.com/open_source/?HTML_Tidy_for_Windows获得了libtidy.dll,它似乎是当前版本。

这是一个控制台应用程序,用于演示我遇到的问题:

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

namespace ConsoleApplication5
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct TidyBuffer
        {
            public IntPtr bp;         // Pointer to bytes
            public uint size;         // # bytes currently in use
            public uint allocated;    // # bytes allocated
            public uint next;         // Offset of current input position
        };

        [DllImport("libtidy.dll")]
        public static extern int tidyBufAlloc(ref TidyBuffer tidyBuffer, uint allocSize);


        static void Main(string[] args)
        {
            Console.WriteLine(CleanHtml("<html><body><p>Hello World!</p></body></html>"));
        }

        static string CleanHtml(string inputHtml)
        {
            byte[] inputArray = Encoding.UTF8.GetBytes(inputHtml);
            byte[] inputArray2 = Encoding.UTF8.GetBytes(inputHtml);

            TidyBuffer tidyBuffer2;
            tidyBuffer2.size = 0;
            tidyBuffer2.allocated = 0;
            tidyBuffer2.next = 0;
            tidyBuffer2.bp = IntPtr.Zero;

            //
            // tidyBufAlloc overwrites inputArray2... why? how? seems like
            // tidyBufAlloc is stomping on the stack a bit too much... but
            // how? I've tried changing the calling convention to cdecl and
            // stdcall but no change.
            //
            Console.WriteLine((inputArray2 == null ? "Array2 null" : "Array2 not null"));
            tidyBufAlloc(ref tidyBuffer2, 65535);
            Console.WriteLine((inputArray2 == null ? "Array2 null" : "Array2 not null"));
            return "did nothing";
        }
    }
}

总而言之,我有点难过。任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:3)

您正在使用TidyBuffer结构的旧定义。新结构更大,因此当您调用allocate方法时,它会覆盖inputArray2的堆栈位置。新定义是:

    [StructLayout(LayoutKind.Sequential)]        
    public struct TidyBuffer        
    {
        public IntPtr allocator;  // Pointer to custom allocator            
        public IntPtr bp;         // Pointer to bytes            
        public uint size;         // # bytes currently in use            
        public uint allocated;    // # bytes allocated            
        public uint next;         // Offset of current input position        
    };        

答案 1 :(得分:2)

为了它的价值,我们在工作中尝试了Tidy并切换到HtmlAgilityPack。

答案 2 :(得分:0)

尝试将tidyBufAlloc声明更改为:

[DllImport("libtidy.dll", CharSet = CharSet.Ansi)]
private static extern int tidyBufAlloc(ref TidyBuffer Buffer, int allocSize);

注意CharSet.Ansi的添加和“int allocSize”(而不是uint)。

另外,请参阅此sample code以获取在C#中使用HTML Tidy的示例。

在你的例子中,如果inputHTML很大,比如50K,inputArray和inputArray2也将是50K。

然后您还尝试在tidyBufAlloc调用中分配65K。

如果指针未正确初始化,则很可能使用随机的.NET堆地址。因此,重写部分或全部看似无关的变量/缓冲区。只是好运,或者您已经分配了大缓冲区,您没有覆盖可能导致无效内存访问错误的代码块。