uboot1.1.6環境變數

来源:https://www.cnblogs.com/053179hu/archive/2019/03/29/10619313.html
-Advertisement-
Play Games

學習目標: 1、分析u-boot-1.1.6環境變數,瞭解環境變數初始化、設置以及過程 2、為後面能夠掌握u-boot-1.1.6如何啟動內核過程打下基礎 1、環境變數的概念 在分析uboot環境變數的源碼實現之前,先介紹一下環境變數的概念。u-boot通過環境變數為用戶提供一定程度的可配置性,在不 ...


學習目標:

1、分析u-boot-1.1.6環境變數,瞭解環境變數初始化、設置以及過程

2、為後面能夠掌握u-boot-1.1.6如何啟動內核過程打下基礎


1、環境變數的概念

在分析uboot環境變數的源碼實現之前,先介紹一下環境變數的概念。u-boot通過環境變數為用戶提供一定程度的可配置性,在不改變源碼、不重新編譯的情況下,可以通過設置環境變數的值來改變uboot的一些設置,如bootdelay時間,內核啟動命令參數等。可配置性意味著環境變數是可以添加、刪除和修改的,也就是說環境變數的內容可能會頻繁變化,為了不讓這種變化對u-boot的代碼和數據造成破壞,通常的選擇是在FLASH中預留一個專門用來存儲環境變數的塊。開發者在串口終端輸入setenv命令可以設置環境量值,設置完成後使用saveenv命令將setenv命令設置好的環境變數保存在非易失存儲器中。如下圖所示,使用uboot時, 在串口終端輸入printenv命令便能夠列印uboot中的環境變數值。

2、環境變數的數據結構

#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)

typedef    struct environment_s {
    unsigned long    crc;        /* CRC32 over data bytes    */
#ifdef CFG_REDUNDAND_ENVIRONMENT
    unsigned char    flags;        /* active/obsolete flags    */
#endif
    unsigned char    data[ENV_SIZE]; /* Environment data        */
} env_t;

crc變數保存上一次環境變數數據寫入flash時做crc運算的結果,當重新從flash中讀取環境變數值時,代碼會對讀取數據進行crc校驗,並將新的校驗的值與上次保存的值進行比較,如果兩次crc校驗值相同,表示存放在flash中的環境變數正常,否則,表示存放環境變數數據損壞。

數組data[ENV_SIZE]保存環境變數的值,環境變數名已經環境變數值以字元形式存放在記憶體中

3、環境變數的初始化

int  env_init(void)
{
/* 未定義CONFIG_OMAP2420H4,此處代碼不被編譯 */
#ifdef CONFIG_OMAP2420H4
    int flash_probe(void);

    if(flash_probe() == 0)
        goto bad_flash;
#endif
    /* env_t *env_ptr = (env_t *)CFG_ENV_ADDR, 此處執行嗎?,如果此處執行 */
    /* 先進入nor flash讀取環境變數,通過CRC檢測讀取環境變數是否正確 */
    if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
        gd->env_addr  = (ulong)&(env_ptr->data);
        gd->env_valid = 1;
        return(0);
    }
/* 未定義CONFIG_OMAP2420H4,此處代碼不被編譯 */
#ifdef CONFIG_OMAP2420H4
bad_flash:
#endif    
    /* CRC校驗後讀取環境變數不正確,此處執行,使用預設環境變數 */
    gd->env_addr  = (ulong)&default_environment[0];
    gd->env_valid = 0;
    return (0);
}

int env_inti(void)函數在uboot代碼第二階段入口函數start_armboot開始處被調用。首先,env_init函數從存放環境變數的flash記憶體地址中讀取環境變數的值,並且對讀出的環境變數數據進行crc校驗,將新校驗的crc值與寫入環境變數時的crc校驗值進行比較。如果兩個值相等,將讀取數據的地址賦值給gd指針執行的全局數據結構中的env_addr成員,並將env_valid標誌位置位。如果讀取環境變數校驗值和寫入時的校驗值不同,存儲在flash中的環境變數值損壞,將預設環境變數值賦給gd指針執行的全局數據結構中的env_addr成員。

env_ptr指針指向環境變數存放初始地址,env_t *env_ptr = (env_t *)CFG_ENV_ADDR。CFG_ENV_ADDR是一個巨集,代表環境變數在flash中存放的地址,由於我配置uboot時目標板是smdk2410,所以該巨集存放在include/configs/smdk2410.h頭文件中。預設環境變數值如下所示:

uchar default_environment[] = {
#ifdef    CONFIG_BOOTARGS
    "bootargs="    CONFIG_BOOTARGS            "\0"
#endif
#ifdef    CONFIG_BOOTCOMMAND
    "bootcmd="    CONFIG_BOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_RAMBOOTCOMMAND
    "ramboot="    CONFIG_RAMBOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_NFSBOOTCOMMAND
    "nfsboot="    CONFIG_NFSBOOTCOMMAND        "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    "bootdelay="    MK_STR(CONFIG_BOOTDELAY)    "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
    "baudrate="    MK_STR(CONFIG_BAUDRATE)        "\0"
#endif
#ifdef    CONFIG_LOADS_ECHO
    "loads_echo="    MK_STR(CONFIG_LOADS_ECHO)    "\0"
#endif
#ifdef    CONFIG_ETHADDR
    "ethaddr="    MK_STR(CONFIG_ETHADDR)        "\0"
#endif
#ifdef    CONFIG_ETH1ADDR
    "eth1addr="    MK_STR(CONFIG_ETH1ADDR)        "\0"
#endif
#ifdef    CONFIG_ETH2ADDR
    "eth2addr="    MK_STR(CONFIG_ETH2ADDR)        "\0"
#endif
#ifdef    CONFIG_ETH3ADDR
    "eth3addr="    MK_STR(CONFIG_ETH3ADDR)        "\0"
#endif
#ifdef    CONFIG_IPADDR
    "ipaddr="    MK_STR(CONFIG_IPADDR)        "\0"
#endif
#ifdef    CONFIG_SERVERIP
    "serverip="    MK_STR(CONFIG_SERVERIP)        "\0"
#endif
#ifdef    CFG_AUTOLOAD
    "autoload="    CFG_AUTOLOAD            "\0"
#endif
#ifdef    CONFIG_PREBOOT
    "preboot="    CONFIG_PREBOOT            "\0"
#endif
#ifdef    CONFIG_ROOTPATH
    "rootpath="    MK_STR(CONFIG_ROOTPATH)        "\0"
#endif
#ifdef    CONFIG_GATEWAYIP
    "gatewayip="    MK_STR(CONFIG_GATEWAYIP)    "\0"
#endif
#ifdef    CONFIG_NETMASK
    "netmask="    MK_STR(CONFIG_NETMASK)        "\0"
#endif
#ifdef    CONFIG_HOSTNAME
    "hostname="    MK_STR(CONFIG_HOSTNAME)        "\0"
#endif
#ifdef    CONFIG_BOOTFILE
    "bootfile="    MK_STR(CONFIG_BOOTFILE)        "\0"
#endif
#ifdef    CONFIG_LOADADDR
    "loadaddr="    MK_STR(CONFIG_LOADADDR)        "\0"
#endif
#ifdef  CONFIG_CLOCKS_IN_MHZ
    "clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
    "pcidelay="    MK_STR(CONFIG_PCI_BOOTDELAY)    "\0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "\0"
};

4、環境變數的重定位

env_inti函數在初始化時對環境變數保存位置進行識別,並將環境變數存放的初始地址賦值給了全局數據結構gt_t成員env_addr。讀取存放在flash中的環境變數數據,進行crc校驗後有效,便使用flash中的環境變數,否則使用預設環境變數。我們知道flash操作速度記憶體慢(若環境變數存放在nand flash中,進行讀取時還需要相應的指令),對flash中環境變數進行直接讀寫不僅會影響uboot性能,而且多次擦除還會影響flash的使用壽命,因此,需要將flash中的環境變數進行重定位操作。

void env_relocate (void)
{
    DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
        gd->reloc_off);

/* 未定義CONFIG_AMIGAONEG3SE巨集,此處不被編譯 */
#ifdef CONFIG_AMIGAONEG3SE
    enable_nvram();
#endif

/* 未定義ENV_IS_EMBEDDED巨集,此處不被編譯 */
#ifdef ENV_IS_EMBEDDED
    /*
     * The environment buffer is embedded with the text segment,
     * just relocate the environment pointer
     */
    env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
    DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
/* ifdef語句為假,此處被編譯 */
#else
    /*
     * We must allocate a buffer for the environment
     */
    /* 在malloc區,為環境變數分配存儲空間 */
    env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif

    /*
     * After relocation to RAM, we can always use the "memory" functions
     */
    env_get_char = env_get_char_memory;

    if (gd->env_valid == 0) {
#if defined(CONFIG_GTH)    || defined(CFG_ENV_IS_NOWHERE)    /* Environment not changable */
        puts ("Using default environment\n\n");
#else
        puts ("*** Warning - bad CRC, using default environment\n\n");
        SHOW_BOOT_PROGRESS (-1);
#endif

        if (sizeof(default_environment) > ENV_SIZE)
        {
            puts ("*** Error - default environment is too large\n\n");
            return;
        }

        memset (env_ptr, 0, sizeof(env_t));
        memcpy (env_ptr->data,
            default_environment,
            sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
        env_ptr->flags = 0xFF;
#endif
        env_crc_update ();
        gd->env_valid = 1;
    }
    else {
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);
/* 未定義CONFIG_AMIGAONEG3SE巨集,此處代碼不被編譯 */
#ifdef CONFIG_AMIGAONEG3SE
    disable_nvram();
#endif
}

void env_relocate (void)函數功能是對flash中環境變數的數據進行重定位,首先在uboot自定義的堆區為環境變數分配相應的記憶體空間,env_ptr指針指向這塊記憶體的初始地址,然後根據gd->env_valid值執行不同的操作(gd->env_valid在env_init函數中設置)。gd->env_valid=0代表flash中環境變數無效,使用預設環境變數,將env_ptr指針指向的記憶體地址清零,再將存放預設環境變數default_environment數組內容拷貝到env_ptr指針指向的記憶體空間中;gd->env_valid=1時flash中存放環境變數有效,調用env_relocate_spec ()函數,將flash中存放的環境變數數據複製到env_ptr指針指向的記憶體空間。最後,將環境變數中的數據在重定位記憶體中的地址賦給gd->env_addr。

5、讀取環境變數

前面介紹過在串口終端輸入printenv,執行回車後,列印環境變數值。執行printenv命令其實底層調用的是do_printenv函數,這個函數源碼如下:

int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    int i, j, k, nxt;
    int rcode = 0;

    if (argc == 1) {        /* 列印所有的環境變數    */
        for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
            for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
                ;
            for (k=i; k<nxt; ++k)
                putc(env_get_char(k));
            putc  ('\n');

            if (ctrlc()) {
                puts ("\n ** Abort\n");
                return 1;
            }
        }

        printf("\nEnvironment size: %d/%d bytes\n", i, ENV_SIZE);

        return 0;
    }
  /* 遍歷輸入的參數,分別列印其環境變數值 */
    for (i=1; i<argc; ++i) {    /* print single env variables    */
        char *name = argv[i];

        k = -1;
     
        for (j=0; env_get_char(j) != '\0'; j=nxt+1) {

            for (nxt=j; env_get_char(nxt) != '\0'; ++nxt)  /* uchar (*env_get_char)(int) = env_get_char_init,env_get_char_init函數獲取相對環境變數記憶體起始地址偏移nxt長度字元 */
                ;
            k = envmatch((uchar *)name, j);  /* 檢測輸入參數名和記憶體中環境變數參數名是否匹配 */
            if (k < 0) {
                continue;
            }
            puts (name);
            putc ('=');
            while (k < nxt)
                putc(env_get_char(k++));
            putc ('\n');
            break;
        }
        if (k < 0) {
            printf ("## Error: \"%s\" not defined\n", name);
            rcode ++;
        }
    }
    return rcode;
}

1、當argc==1時(例如執行printenv命令,argc=1),列印uboot所有的環境變數

2、當argc>1時(例如執行printenv bootcmd baudrate命令,argc=3),列印環境變數bootcmd、baudrate

6、設置環境變數

同讀取環境變數一樣,執行setenv命令其實底層調用的是_do_setenv函數,這個函數源碼如下:

int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    if (argc < 2) {
        printf ("Usage:\n%s\n", cmdtp->usage);
        return 1;
    }

    return _do_setenv
(flag, argc, argv); } int _do_setenv (int flag, int argc, char *argv[]) { int i, len, oldval; int console = -1; uchar *env, *nxt = NULL; char *name; bd_t *bd = gd->bd;
  /* env_data獲取環境變數的數據部分在記憶體的初始地址 */ uchar
*env_data = env_get_addr(0); if (!env_data) /* need copy in RAM */ return 1; name = argv[1];   
  /* 檢測輸入的第二個參數為'=',列印相關錯誤信息 */
if (strchr(name, '=')) { printf ("## Error: illegal character '=' in variable name \"%s\"\n", name); return 1; }
  /* 遍歷記憶體中的環境變數數據數組,檢測配置環境變數名是否存在 */
oldval = -1; for (env=env_data; *env; env=nxt+1) { for (nxt=env; *nxt; ++nxt) ; if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0) break; } /* * Delete any existing definition */ if (oldval >= 0) { #ifndef CONFIG_ENV_OVERWRITE
/* 網卡地址和串口號是固定的,如果設置是網卡地址和串口號,列印"Can't overwrite \"%s\"\n", name,代碼返回 */
if ( (strcmp (name, "serial#") == 0) || ((strcmp (name, "ethaddr") == 0) #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0) #endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ ) ) { printf ("Can't overwrite \"%s\"\n", name); return 1; } #endif /* Check for console redirection */ if (strcmp(name,"stdin") == 0) { console = stdin; } else if (strcmp(name,"stdout") == 0) { console = stdout; } else if (strcmp(name,"stderr") == 0) { console = stderr; } if (console != -1) { if (argc < 3) { /* Cannot delete it! */ printf("Can't delete \"%s\"\n", name); return 1; } /* Try assigning specified device */ if (console_assign (console, argv[2]) < 0) return 1; #ifdef CONFIG_SERIAL_MULTI if (serial_assign (argv[2]) < 0) return 1; #endif } /* 如果是參數=波特率,重新設置相應波特率值 */ if (strcmp(argv[1],"baudrate") == 0) { int baudrate = simple_strtoul(argv[2], NULL, 10); int i;
       /* 檢測輸入波特率值是否合法,如果不合法,列印錯誤,返回函數 */
for (i=0; i<N_BAUDRATES; ++i) { if (baudrate == baudrate_table[i]) break; } if (i == N_BAUDRATES) { printf ("## Baudrate %d bps not supported\n", baudrate); return 1; } printf ("## Switch baudrate to %d bps and press ENTER ...\n", baudrate); udelay(50000); gd->baudrate = baudrate; #ifdef CONFIG_PPC gd->bd->bi_baudrate = baudrate; #endif serial_setbrg (); udelay(50000); for (;;) { if (getc() == '\r') break; } } if (*++nxt == '\0') { if (env > env_data) { env--; } else { *env = '\0'; } } else { for (;;) { *env = *nxt++; if ((*env == '\0') && (*nxt == '\0')) break; ++env; } } *++env = '\0'; } #ifdef CONFIG_NET_MULTI if (strncmp(name, "eth", 3) == 0) { char *end; int num = simple_strtoul(name+3, &end, 10); if (strcmp(end, "addr") == 0) { eth_set_enetaddr(num, argv[2]); } } #endif /* Delete only ? */ if ((argc < 3) || argv[2] == NULL) { env_crc_update (); return 0; } /* * Append new definition at the end */ for (env=env_data; *env || *(env+1); ++env) ; if (env > env_data) ++env; /* * Overflow when: * "name" + "=" + "val" +"\0\0" > ENV_SIZE - (env-env_data) */ len = strlen(name) + 2; /* add '=' for first arg, ' ' for all others */ for (i=2; i<argc; ++i) { len += strlen(argv[i]) + 1; } if (len > (&env_data[ENV_SIZE]-env)) { printf ("## Error: environment overflow, \"%s\" deleted\n", name); return 1; } while ((*env = *name++) != '\0') env++; for (i=2; i<argc; ++i) { char *val = argv[i]; *env = (i==2) ? '=' : ' '; while ((*++env = *val++) != '\0')  /* 將設置的新的參數寫入到重定位的環境變數數據中 */ ; } /* end is marked with double '\0' */ *++env = '\0'; /* Update CRC */ env_crc_update ();   /* 如果設置環境變數是ethaddr、ipaddr、loadaddr、bootfile、vga_fg_color 根據新的環境值,立即更新相應全局變數、更新底層硬體 */ if (strcmp(argv[1],"ethaddr") == 0) { char *s = argv[2]; /* always use only one arg */ char *e; for (i=0; i<6; ++i) { bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0; if (s) s = (*e) ? e+1 : e; } #ifdef CONFIG_NET_MULTI eth_set_enetaddr(0, argv[2]); #endif return 0; } if (strcmp(argv[1],"ipaddr") == 0) { char *s = argv[2]; /* always use only one arg */ char *e; unsigned long addr; bd->bi_ip_addr = 0; for (addr=0, i=0; i<4; ++i) { ulong val = s ? simple_strtoul(s, &e, 10) : 0; addr <<= 8; addr |= (val & 0xFF); if (s) s = (*e) ? e+1 : e; } bd->bi_ip_addr = htonl(addr); return 0; } if (strcmp(argv[1],"loadaddr") == 0) { load_addr = simple_strtoul(argv[2], NULL, 16); return 0; } #if (CONFIG_COMMANDS & CFG_CMD_NET) if (strcmp(argv[1],"bootfile") == 0) { copy_filename (BootFile, argv[2], sizeof(BootFile)); return 0; } #endif /* CFG_CMD_NET */ #ifdef CONFIG_AMIGAONEG3SE if (strcmp(argv[1], "vga_fg_color") == 0 || strcmp(argv[1], "vga_bg_color") == 0 ) { extern void video_set_color(unsigned char attr); extern unsigned char video_get_attr(void); video_set_color(video_get_attr()); return 0; } #endif /* CONFIG_AMIGAONEG3SE */ return 0; }

7、保存設置的環境變數

串口輸入setenv命令,系統是將新的環境變數保存到重定位的記憶體中,我們知道記憶體在系統掉電後數據丟失,如果想要將設置的環境變數永久保存,還是要將新的環境變數寫入到存儲環境變數的flash地址中。執行saveenv命令就是將分配記憶體中的環境變數保存到flash中,執行saveenv命令其實底層調用的是do_printenv函數,這個函數源碼如下:

int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    extern char * env_name_spec;

    printf ("Saving Environment to %s...\n", env_name_spec);

    return (saveenv() ? 1 : 0);
}

/* 由於saveenv函數過長,下麵刪去不被包含的巨集內的代碼 */
int saveenv(void) { int len, rc; ulong end_addr; ulong flash_sect_addr; uchar *env_buffer = (uchar *)env_ptr; int rcode = 0;
  /*
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; */ flash_sect_addr = (ulong)flash_addr; /* flash_sect_addr 保持環境變數在flash中起始地址 */ len = CFG_ENV_SIZE; end_addr = flash_sect_addr + 0x20000 - 1; debug ("Protect off %08lX ... %08lX\n", (ulong)flash_sect_addr, end_addr); if (flash_sect_protect (0, flash_sect_addr, end_addr)) return 1; puts ("Erasing Flash..."); if (flash_sect_erase (flash_sect_addr, end_addr)) return 1; puts ("Writing to Flash... "); rc = flash_write((char *)env_buffer, flash_sect_addr, len); if (rc != 0) { flash_perror (rc); rcode = 1; } else { puts ("done\n"); } /* try to re-protect */ (void) flash_sect_protect (1, flash_sect_addr, end_addr); return rcode; }

1、env_buffer指針存放環境變數在記憶體中的初始位置

2、寫入flash之前應該擦除想要寫入的扇區,調用flash_sect_erase函數擦除環境變數所做flash的扇區

3、再調用flash_write,寫入新的環境變數


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • DS密法是DYLIKE本人研發的一種針對文本字元串的高強度加密方法,本加密方法的優點是同源不同密,同一個源文本每次加密的結果都不同,長度也不同.密鑰最大可達String類型的字元最大長度.缺點是解密時間長,500字長度的密文解密約需半秒.適用於非頻繁的數據加密交換.註意,欲加密的源字串不能少於5字. ...
  • 今天應客戶要求,修改導出word模板。使用的是Aspose.Words插件。這個程式原是同事所寫,且自己對Aspose不是很瞭解。在替換木板上花費了一些時間。 先來一張圖:下圖是原來的模板。現在要求刪除“原始內容3”,在“原始內容1”上面添加“新內容”,假設新加“新內容”的分數占位符是“Rule1” ...
  • 第一步下載erlang環境並安裝: rpm -Uvh https://mirrors.aliyun.com/centos/7.6.1810/extras/x86_64/Packages/epel-release-7-11.noarch.rpm yum install erlang 第二步下載Rabb ...
  • 環境:MacOS 10.13.6 虛擬機:VirtualBox6.0(VirtualBox-6.0.4-128413-OSX.dmg) Linux:Centos7(CentOS-7-x86_64-Minimal-1810.iso) 一:虛擬機安裝 這個。。。。真沒啥好說的。。。略過 二:虛擬機創建C ...
  • 1. 主機規劃 Pillar文檔 註意事項 2. Grains VS Pillar 3. Pillar基本信息 4. 顯示系統自帶的pillar 系統自帶的pillar預設是不顯示的 註意:看完之後還原回去,因為數據較多。和自定義數據雜在一起,不方便查看 4.1. 修改配置文件並重啟服務 4.2. ...
  • 1. 主機規劃 Grains文檔 註意事項 修改了master或者minion的配置文件,那麼必須重啟對應的服務。 2. Grains基本信息 3. Grains優先順序信息 推薦使用 自編寫py腳本定義,這樣自定義的grains只需要在master端維護即可,減少後期維護成本。 下文內容就是根據優先 ...
  • 註: client 個人覺得沒必要用 libvent來實現就沒寫 註:由於 涉及到回調函數的使用 如有疑惑的可以先去瞭解回調函數的實現機理 先來說一下 libevent主要是幹啥的 : 內部封裝了 socket 通信 和 io 轉接函數 ,通過使用大量的回調使得不用考慮大量重覆的工作,並且其根據不同 ...
  • . linux Linux是一套免費使用和自由傳播的類Unix操作系統,是一個基於POSIX和UNIX的多用戶、多任務、支持多線程和多CPU的操作系統。它能運行主要的UNIX工具軟體、應用程式和網路協議。它支持32位和64位硬體。Linux繼承了Unix以網路為核心的設計思想,是一個性能穩定的多用戶 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...