.NET Framework项目中获取剪切板内容乱码问题解决

本文讲述了在开发中遇到的剪贴板文本乱码问题,源于不同编码冲突。作者通过C语言创建dll库,实现从UTF-8转换为GBK编码,然后.NET通过DllImport调用该库获取正确格式的剪贴板内容。

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

问题描述:

在开发过程中,需要实现获取剪切板文本的方法,使用Clipboard.GetDataObject有时候会乱码

           // GetDataObject获取当前剪贴板上的数据
           IDataObject data = Clipboard.GetDataObject();

           // 将数据与指定的格式进行匹配,返回bool
           if (data.GetDataPresent(DataFormats.Text))
           {
               string text = (string)data.GetData(DataFormats.Text);

经测试发现,乱码的文本是UTF-8编码的,而中文的电脑上默认是GBK编码,所以获取的剪切板内容转换为string的过程中错误地将UTF-8编码的文本按照GBK编码解析,导致了乱码。

解决方法:

既然.NET获取的剪切板是string,那么我们只要获取剪切板的实际字节,检测它的编码,然后将它转换回GBK编码,因为我不知道如何用.NET获取剪切板实际数据,所以采用C语言实现获取剪切板原始内容,然后.NET调用C语言的dll库实现获取剪切板内容,代码如下:

C语言实现clipget.dll

#include <Windows.h>
#include <stdio.h>

//判断字符串是否为GBK
int isGBK(unsigned char* buf, int size) {
    int start = 0;
    int end = size;
    int c = 0;
    int type = 0;
    
    while (start < end) {
        c = buf[start];
        
        switch(type) {
            case 0:
                if (c >= 0x81 && c <= 0xfe) {
                    type = 1;
                } else if (c < 128) {
                    // ASCII character
                } else {
                    return 0; // Not a valid GBK character
                }
                break;
            case 1:
                if (c != 0x7f && c >= 0x40 && c <= 0xfe) {
                    type = 0;
                } else {
                    return 0; // Not a valid GBK character
                }
                break;
        }
        
        start++;
    }
    
    return 1; // All characters are valid GBK characters
}

int isGB2312(unsigned char* buf, int size) {
    int isGBK = 1;
    int start = 0;
    int end = size;
    int c = 0;
    int c2 = 0;
    int type = 0;
    unsigned char buffer[100];
    int i = 0;
    
    while (start < end) {
        c = buf[start];
        buffer[i++] = c;
        switch (type) {
            case 0:
                if (c >= 0xa1 && c <= 0xf7) {
                    type = 1;
                } else if (c < 128) {
                    
                } else {
                    isGBK = 0;
                    goto GBWHILE;
                }
                break;
            case 1:
                if (c >= 0xa1 && c <= (0xa0 + 94)) {
                    type = 0;
                } else {
                    isGBK = 0;
                    goto GBWHILE;
                }
                break;
        }
        
        start++;
    }
    
    GBWHILE:
    // printf("isGb: %d\n", start);
    return isGBK;
}

//将UTF-8转换为GBK
char* utf8_to_gbk(char* utf8) {
    int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
    wchar_t* wstr = (wchar_t*)malloc(sizeof(wchar_t) * len);
    MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
    len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* str = (char*)malloc(sizeof(char) * len);
    WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
    free(wstr);
    return str;
}

__declspec(dllexport) char* clipget(){
    // printf("剪贴板数据获取\n");
    // 打开剪贴板
    if (!OpenClipboard(NULL)) {
        // printf("无法打开剪贴板\n");
        return "";
    }

    // 获取剪贴板中的文本数据
    HANDLE handle = GetClipboardData(CF_TEXT);
    if (handle == NULL) {
        // printf("无法获取剪贴板文本数据\n");
        return "";
    }

    // 锁定内存并获取剪贴板文本
    char* temp = (char*)GlobalLock(handle);
    char* text = NULL;
    //复制temp中的内容到text中
    text = (char*)malloc(strlen(temp) + 1);
    strcpy(text, temp);
    if (text == NULL) {
        // printf("无法锁定内存\n");
        return "";
    }
    //判断文本是否为GBK编码
    if (isGB2312(text, strlen(text))) {
        // printf("文本为GBK编码\n");
    } else {
        //转换编码为GBK
        char* gbk = utf8_to_gbk(text);
        //判断gbk中?数量是否大于2,如果不大于,就赋值给text,否则释放
        int len = strlen(gbk);
        int count = 0;
        for(int i=0;i<len;i++){
            if(gbk[i] == '?'){
                count++;
            }
        }
        if(count<=2){
            text = gbk;
        }else{
            free(gbk);
        }
        
    }
    

    // 输出剪贴板文本数据
    // printf("%s\n", text);

    // 解锁内存并关闭剪贴板
    GlobalUnlock(handle);
    CloseClipboard();
    return text;
}
/*
int main() {
    // printf("剪贴板数据获取\n");
    // 打开剪贴板
    if (!OpenClipboard(NULL)) {
        // printf("无法打开剪贴板\n");
        return 1;
    }

    // 获取剪贴板中的文本数据
    HANDLE handle = GetClipboardData(CF_TEXT);
    if (handle == NULL) {
        // printf("无法获取剪贴板文本数据\n");
        return 1;
    }

    // 锁定内存并获取剪贴板文本
    char* text = (char*)GlobalLock(handle);
    if (text == NULL) {
        // printf("无法锁定内存\n");
        return 1;
    }
    //判断文本是否为GBK编码
    if (isGB2312(text, strlen(text))) {
        // printf("文本为GBK编码\n");
    } else {
        //转换编码为GBK
        char* gbk = utf8_to_gbk(text);
        text = gbk;
    }
    

    // 输出剪贴板文本数据
    printf("%s\n", text);

    // 解锁内存并关闭剪贴板
    GlobalUnlock(handle);
    CloseClipboard();

    return 0;
}
*/

.NET调用clipget.dll

        // 声明 clipget 方法的签名
        [DllImport("clipget.dll")]
        private static extern IntPtr clipget();


        public string getClipString()
        {
            // 调用 clipget 方法并获得返回的 char* 指针
            IntPtr ptr = clipget();

            // 将 char* 指针转换为 string
            string result = Marshal.PtrToStringAnsi(ptr);

            // 释放内存
            //Marshal.FreeCoTaskMem(ptr);

            return result;
        }

调用方法

string dllPath = "clipget.dll";
if (File.Exists(dllPath))
{
    string text = getClipString();
    //将剪切板内容赋值到控件上
    textBox_input.Text = text;
}
else
{
  // ... 如果dll库不存在,.NET实现
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值