【问题标题】:Working with pointer offsets in C#在 C# 中使用指针偏移量
【发布时间】:2011-04-22 17:17:29
【问题描述】:

我一直在开发一个使用 API 从 Windows 事件日志中获取事件的应用程序。我现在被指针偏移量困住了。我正在使用的特定结构是 EVENTLOGRECORD(请参阅:http://msdn.microsoft.com/en-us/library/aa363646(v=vs.85).aspx)。我的 C# 结构定义为:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
internal struct EVENTLOGRECORD
{
    internal UInt32 Length;
    internal UInt32 Reserved;
    internal UInt32 RecordNumber;
    internal UInt32 TimeGenerated;
    internal UInt32 TimeWritten;
    internal UInt32 EventID;
    internal UInt16 EventType;
    internal UInt16 NumStrings;
    internal UInt16 EventCategory;
    internal UInt16 ReservedFlags;
    internal UInt32 ClosingRecordNumber;
    internal UInt32 StringOffset;
    internal UInt32 UserSidLength;
    internal UInt32 UserSidOffset;
    internal UInt32 DataLength;
    internal UInt32 DataOffset;
}

我的 ReadEventLog 函数声明为:

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "ReadEventLog")]
internal static extern Boolean ReadEventLog(IntPtr hEventLog, EVT_READ_FLAGS dwReadFlags, UInt32 dwRecordOffset, IntPtr lpBuffer, UInt32 nNumberOfBytesToRead, out UInt32 pnBytesRead, out UInt32 pnMinNumberOfBytesNeeded);

我能够获取填充数据的结构,并且可以使用 IntPtr.Add 访问 SourceName 和 ComputerName 部分。示例:

IntPtr pSrc = IntPtr.Add(pRecord, Marshal.SizeOf(typeof(EVENTLOGRECORD)));
string sSrc = Marshal.PtrToStringAuto(pSrc);
Console.WriteLine("source: {0}\n", sSrc);

IntPtr pComp = IntPtr.Add(pSrc, (sSrc.Length * 2) + 2);
string sComp = Marshal.PtrToStringAuto(pComp);
Console.WriteLine("computer: {0}\n", sComp);

我的问题是尝试从结构中获取字符串部分。我似乎无法弄清楚正确的偏移量是多少。我可以在 C++ 中做到这一点,但我似乎无法让它在 C# 中工作。这是我在 C++ 中使用的 sn-p(elr 是 (EVENTLOGRECORD*)pRecord):

char* strings = (LPSTR)((LPBYTE) elr + elr->StringOffset);
while (elr->NumStrings)
{
    wprintf(L"String: %s\n", strings);
    strings += (wcslen((wchar_t*)strings) * sizeof(wchar_t)) + sizeof(wchar_t);
    elr->NumStrings--;
}

希望有人可以帮助解释我所缺少的。我也很好奇是否有 IntPtr.Add 的替代品,因为这需要 .NET 4.0。无论如何,我都不是 p/invoke 方面的专家。谢谢。

【问题讨论】:

  • 这是作为练习吗?为什么不使用 System.Diagnostics.EventLog 类来读取条目?
  • 没有。实际上它是由于 .NET 框架中的一个错误而完成的:connect.microsoft.com/VisualStudio/feedback/details/654644/… 我提交了那个,显然这确实是一个问题。否则,我会在 EventLog 类中到处都是。这要容易得多。
  • 我真的不明白为什么写入错误(似乎是 Connect 链接)会阻止您使用内置类从日志中读取事件。
  • @Will Dean - 你不得不说两次?我的最终目标是监控事件。您不能在框架中使用 EntryWritten 可靠地做到这一点。所以我已经使用 API 来提供所需的功能。

标签: c# pointers pinvoke


【解决方案1】:

如果您使用 Marshal.PtrToStructure() 将数据块的第一部分复制到 EVENTLOGRECORD,那么您应该能够执行以下操作:

EVENTLOGRECORD record;
... Copy the ptr into record ...
IntPtr pStrings = IntPtr.Add(pRecord, (record.StringOffset * 2));

我很乐意为您解决这个问题,但我懒得执行所有其他 p/invoke 位,因为这些位能够进行 ReadEventLog 调用。

【讨论】:

  • 我可以用 ReadEventLog 很好地填写结构。我也使用:IntPtr pRecord = IntPtr.Zero; object o = Marshal.PtrToStructure(pRecord, typeof(EVENTLOGRECORD)); 我只是看不到所有字符串。 EVENTLOGRECORD 中的 NumStrings 指示的通常不止一个。
【解决方案2】:

在远离编码清理大脑一段时间后,我终于有了一个解决方案。我错误地使偏移量太大,因为我正在获取结构(EVENTLOGRECORD)的大小,将其添加到 StringOffset,然后将其添加到现有对象。我真正需要做的就是将偏移量添加到现有的 IntPtr (pRecord)。不知道为什么之前没有点击。

所以简单地做:

int offset = ((int)(((EVENTLOGRECORD)o).StringOffset));
IntPtr pStrings = IntPtr.Add(pRecord, offset);
string sString = Marshal.PtrToStringAuto(pStrings);
Console.WriteLine("string : {0}\n", sString);

..足以获取字符串。不过感谢您的帮助。我显然缺乏将建议标记为有用的代表。我想我只是需要时间远离这个。

【讨论】:

    【解决方案3】:

    现在问可能为时已晚。为什么不使用 .NET 的 System.Diagnostics.EventLog 类? 据我所知,您对 EventLog 类有疑问。您是否尝试过适用于 Windows Vista 及更高版本的 EventLogReader 类?

    使用 Reflector,您还可以了解它们是如何进行编组的。看起来不可能让编组器完成全部工作。他们确实将数据读入一个简单的字节数组并将其存储在 EventLogEntry 类中,该类直接从字节数组中读取所需的数据。

    读取他们在 EventLogEntry 类中所做的替换字符串。 databuf 成员是来自本机 ReadEventLog 调用的字节数组。

    [MonitoringDescription("LogEntryReplacementStrings")]
    public string[] ReplacementStrings
    {
        get
        {
            string[] strArray = new string[this.ShortFrom(this.dataBuf, this.bufOffset + 0x1a)];
            int index = 0;
            int offset = this.bufOffset + this.IntFrom(this.dataBuf, this.bufOffset + 0x24);
            StringBuilder builder = new StringBuilder();
            while (index < strArray.Length)
            {
                char ch = this.CharFrom(this.dataBuf, offset);
                if (ch != '\0')
                {
                    builder.Append(ch);
                }
                else
                {
                    strArray[index] = builder.ToString();
                    index++;
                    builder = new StringBuilder();
                }
                offset += 2;
            }
            return strArray;
        }
    }
    

    你的, 阿洛伊斯克劳斯

    【讨论】:

    • @Deviation - 事件日志条目写入中的一些模糊错误没有理由不在框架源中寻找阅读事件日志条目的灵感。 Alois 刚刚发布的代码只是做了你想做的事情。
    • @Will Dean - 一些不为人知的错误?如果您无法使用框架可靠地读取事件,则必须以另一种方式进行。 EntryWritten 事件有一个错误,会阻止它被可靠地使用。我回复时他的代码没有发布。我现在正在检查它是否能解决我遇到的问题。
    • @Deviation,对不起,我已经阅读了 Connect 错误和链接的线程,它们似乎都是关于编写事件和后续事件,这些事件并没有可靠地引发。您的问题确实首先说您正在编写一个从日志中“获取”(我认为这是读取)事件的应用程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    • 2021-07-22
    • 2021-06-07
    • 1970-01-01
    • 2013-04-09
    • 1970-01-01
    相关资源
    最近更新 更多