转发2(blogger Ben)PRINTER_NOTIFY_INFO

本文探讨了C#中结构体在32位与64位环境下内存对齐的问题,特别是当使用显式布局时如何确保结构体的兼容性和正确性。通过详细示例,展示了如何创建既能用于32位也能用于64位应用的结构体。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unmanaged Structures, Padding and C#, Part 2

Continuing on from my previous post, we find that we have problems when we try to use our C# structure on a 64bit environment due to the way structures are padded in memory. The other problem that this will lead to is that using an Explicit layout in the structure will also no longer work as the exact layout of the structure in memory will now vary depending on whether or not we are in a 32bit or 64bit environment.

There are two solutions to this problem. You can either create a 32bit and 64bit version of your structure, and probibly make a 32bit and 64bit version of your application, or you can employ a more cunning approach. You probibly do not want to create two versions of your structure, especially if you were not planning on making two versions of your application, so from here on we will look at the approach of making a structure that is compatible with both 32 bit and 64 bit applications.

The first problem that we need to solve is the union part of the C structure. What we will do at this stage is take the biggest part of the union. In a 32 bit environment they are both the same size, but in a 64bit environment the structure with the pointer will be bigger, so we will use this.

The first step is to go ahead and create this internal structure.
 

[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA_DATA
{
  public uint cbBuf;
  public IntPtr pBuf;
}

Following this we now should create a structure to represent the union clause. As mentioned previously because unions involve overlapped data we need to use an explicit layout. The other problem we will get is if we try to set the array of DWORDs adwData to overlap with the PRINTER_NOTIFY_INFO_DATA_DATA structure. This problem occurs because the .Net compiler treats the contained structure as a value type, and the contained array as a reference type, and can not handle them bothe being at the same location in the structure. To get around this I have created a couple of private fields to read the elements of the array and a public property to provide the expected public interface.
 

[StructLayout(LayoutKind.Explicit)]
public struct PRINTER_NOTIFY_INFO_DATA_UNION
{
  [FieldOffset(0)]
  private uint adwData0;
  [FieldOffset(4)]
  private uint adwData1;
  [FieldOffset(0)]
  public PRINTER_NOTIFY_INFO_DATA_DATA Data;
  public uint[] adwData
  {
    get
    {
      return new uint[] { this.adwData0, this.adwData1
    };
  }
}

Now we create the PRINTER_NOTIFY_INFO_DATA structure, and include the above structure in. Note that we are also no longer defining the structure explicitly as we want it to size correctly in both 32nit and 64bit environments.
 

[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_NOTIFY_INFO_DATA
{
  public ushort Type;
  public ushort Field;
  public uint Reserved;
  public uint Id;
  public PRINTER_NOTIFY_INFO_DATA_UNION NotifyData;
}

This will now layout correctly in 32bit and 64bit environments and still provide access to all of the fields defined in the C structure. The new and correct in memory layout of a PRINTER_NOTIFY_INFO structure followed by two PRINTER_NOTIFY_INFO_DATA structure in a 64 bit environment is as follows.
 

0x0000 PRINTER_NOTIFY_INFO.Version
0x0004 PRINTER_NOTIFY_INFO.Flags
0x0008 PRINTER_NOTIFY_INFO.Count
0x000c Padding as PRINTER_NOTIFY_INFO_DATA must start on a 8 byte boundary.
0x0010 PRINTER_NOTIFY_INFO_DATA[0].Type
0x0012 PRINTER_NOTIFY_INFO_DATA[0].Field
0x0014 PRINTER_NOTIFY_INFO_DATA[0].Reserved
0x0018 PRINTER_NOTIFY_INFO_DATA[0].Id
0x001c Padding as PRINTER_NOTIFY_INFO_DATA_DATA must start on a 8 byte boundary.
0x0020 PRINTER_NOTIFY_INFO_DATA[0].Data.cbBuf
0x0024 Padding as a 8 byte Pointer must be on a 8 byte boundary.
0x0028 PRINTER_NOTIFY_INFO_DATA[0].Data.pBuf
0x0030 PRINTER_NOTIFY_INFO_DATA[0].Type
0x0032 PRINTER_NOTIFY_INFO_DATA[0].Field
0x0034 PRINTER_NOTIFY_INFO_DATA[0].Reserved
0x0038 PRINTER_NOTIFY_INFO_DATA[0].Id
0x003c Padding as PRINTER_NOTIFY_INFO_DATA_DATA must start on a 8 byte boundary.
0x0040 PRINTER_NOTIFY_INFO_DATA[0].Data.cbBuf
0x0044 Padding as a 8 byte Pointer must be on a 8 byte boundary.
0x0048 PRINTER_NOTIFY_INFO_DATA[0].Data.pBuf

https://lifeandtimesofadeveloper.blogspot.com/2007/10/unmanaged-structures-padding-and-c-part_18.html

纠错代码#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME_LEN 50 // 定义粉丝结构 typedef struct Fan { char name[MAX_NAME_LEN]; struct Fan* next; } Fan; // 定义博主结构 typedef struct Blogger { char name[MAX_NAME_LEN]; int fan_count; Fan* fans; // 粉丝链表头指针 struct Blogger* next; } Blogger; // 全局变量:博主链表头指针 Blogger* blogger_list = NULL; // 按照粉丝数从高到低展示所有博主信息 void display_blogger_by_fan_count() { if (blogger_list == NULL) { printf("没有博主信息。\n"); return; } // 使用冒泡排序对博主按粉丝数排序 Blogger* current = blogger_list; int swapped; do { swapped = 0; Blogger* temp = blogger_list; while (temp->next != NULL) { if (temp->fan_count < temp->next->fan_count) { // 交换两个节点的数据部分 int count_temp = temp->fan_count; char name_temp[MAX_NAME_LEN]; strcpy(name_temp, temp->name); temp->fan_count = temp->next->fan_count; strcpy(temp->name, temp->next->name); temp->next->fan_count = count_temp; strcpy(temp->next->name, name_temp); swapped = 1; } temp = temp->next; } } while (swapped); // 打印排序后的博主信息 Blogger* ptr = blogger_list; while (ptr != NULL) { printf("博主: %s, 粉丝数: %d\n", ptr->name, ptr->fan_count); ptr = ptr->next; } } // 查询某个博主的粉丝群 void query_fans(char blogger_name[]) { Blogger* ptr = blogger_list; while (ptr != NULL) { if (strcmp(ptr->name, blogger_name) == 0) { printf("博主 %s 的粉丝有:\n", blogger_name); Fan* fan_ptr = ptr->fans; while (fan_ptr != NULL) { printf("%s\n", fan_ptr->name); fan_ptr = fan_ptr->next; } return; } ptr = ptr->next; } printf("未找到博主 %s。\n", blogger_name); } // 加入某个博主的某个粉丝群 void add_fan_to_blogger(char blogger_name[], char fan_name[]) { Blogger* ptr = blogger_list; while (ptr != NULL) { if (strcmp(ptr->name, blogger_name) == 0) { Fan* new_fan = (Fan*)malloc(sizeof(Fan)); strcpy(new_fan->name, fan_name); new_fan->next = ptr->fans; ptr->fans = new_fan; ptr->fan_count++; printf("成功将 %s 添加为博主 %s 的粉丝。\n", fan_name, blogger_name); return; } ptr = ptr->next; } printf("未找到博主 %s。\n", blogger_name); } // 退出某个博主的某个粉丝群 void remove_fan_from_blogger(char blogger_name[], char fan_name[]) { Blogger* ptr = blogger_list; while (ptr != NULL) { if (strcmp(ptr->name, blogger_name) == 0) { Fan* prev = NULL; Fan* current = ptr->fans; while (current != NULL) { if (strcmp(current->name, fan_name) == 0) { if (prev == NULL) { ptr->fans = current->next; } else { prev->next = current->next; } free(current); ptr->fan_count--; printf("成功将 %s 从博主 %s 的粉丝中移除。\n", fan_name, blogger_name); return; } prev = current; current = current->next; } printf("未找到博主 %s 的粉丝 %s。\n", blogger_name, fan_name); return; } ptr = ptr->next; } printf("未找到博主 %s。\n", blogger_name); } // 统计某个博主发布的信息传播的人数 int calculate_spread(char blogger_name[]) { Blogger* ptr = blogger_list; while (ptr != NULL) { if (strcmp(ptr->name, blogger_name) == 0) { return ptr->fan_count; } ptr = ptr->next; } printf("未找到博主 %s。\n", blogger_name); return 0; } // 添加一个博主 void add_blogger(char blogger_name[]) { Blogger* new_blogger = (Blogger*)malloc(sizeof(Blogger)); strcpy(new_blogger->name, blogger_name); new_blogger->fan_count = 0; new_blogger->fans = NULL; new_blogger->next = blogger_list; blogger_list = new_blogger; printf("成功添加博主 %s。\n", blogger_name); } int main() { int choice; char blogger_name[MAX_NAME_LEN], fan_name[MAX_NAME_LEN]; while (1) { printf("\n菜单:\n"); printf("1. 按照粉丝数从高到低展示所有博主信息\n"); printf("2. 查询某个博主的粉丝群\n"); printf("3. 加入某个博主的某个粉丝群\n"); printf("4. 退出某个博主的某个粉丝群\n"); printf("5. 统计某个博主发布的信息传播的人数\n"); printf("6. 添加博主\n"); printf("7. 退出程序\n"); printf("请输入你的选择: "); scanf("%d", &choice); switch (choice) { case 1: display_blogger_by_fan_count(); break; case 2: printf("请输入博主名: "); scanf("%s", blogger_name); query_fans(blogger_name); break; case 3: printf("请输入博主名: "); scanf("%s", blogger_name); printf("请输入粉丝名: "); scanf("%s", fan_name); add_fan_to_blogger(blogger_name, fan_name); break; case 4: printf("请输入博主名: "); scanf("%s", blogger_name); printf("请输入粉丝名: "); scanf("%s", fan_name); remove_fan_from_blogger(blogger_name, fan_name); break; case 5: printf("请输入博主名: "); scanf("%s", blogger_name); int spread = calculate_spread(blogger_name); if (spread > 0) { printf("博主 %s 发布的信息传播人数为: %d\n", blogger_name, spread); } break; case 6: printf("请输入博主名: "); scanf("%s", blogger_name); add_blogger(blogger_name); break; case 7: printf("退出程序。\n"); exit(0); default: printf("无效的选择,请重试。\n"); } } return 0; }
最新发布
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值