簡體   English   中英

C#Char * to String

[英]C# Char* to String

我經常環顧四周,似乎無法找到類似於我正在做的事情的解決方案。 我有兩個應用程序,一個本機C ++應用程序和一個托管C#應用程序。 C ++應用程序分配在內存管理器中使用的字節池。 在此內存管理器中分配的每個對象都有一個標頭,每個標頭都有一個char *,指向給定對象的名稱。 C#app充當此內存的查看器。 我使用內存映射文件允許C#app在C ++應用程序運行時讀取內存。 我的問題是我試圖從頭結構中讀取對象的名稱並將其顯示在C#中(或者只是將它存儲在String中,無論如何)。 使用不安全的代碼我能夠將構成char*的四個字節轉換為IntPtr ,將其轉換為void* ,並調用Marshal.PtrToStringAnsi 這是代碼:

IntPtr namePtr = new IntrPtr(BitConverter.ToInt32(bytes, index));
unsafe
{
    void* ptr = namePtr.ToPointer();
    char* cptr = (char*)ptr;
    output = Marshal.PtrToStringAnsi((IntPtr)ptr);
}

在這種情況下, bytes是從內存映射文件讀取的數組,表示本機應用程序創建的所有字節池, index是name指針的第一個字節的索引。

我已經驗證,在托管方面,調用namePtr.ToPointer()返回的地址正是本機應用程序中名稱指針的地址。 如果這是本機代碼,我只需將ptr轉換為char*就可以了,但是在托管代碼中,我已經閱讀過,我必須使用Marshaller來執行此操作。

此代碼會產生不同的結果。 有時cptr為null,有時它指向\\0 ,有時它指向一些亞洲字符(當通過PtrToStringAnsi方法運行時會產生看似無關的字符)。 我認為它可能是一個fixed東西,但ToPointer產生一個固定的指針。 有時在轉換為char* ,調試器會說Unable to evaluate the expression. The pointer is not valid Unable to evaluate the expression. The pointer is not valid或類似的東西(重現每個變化的東西都不容易)。 有時我在讀取內存時會遇到訪問沖突,導致我進入C ++方面。

在C ++方面,我認為實際讀取內存可能存在一些問題,因為雖然存儲指針的內存是內存映射文件的一部分,但構成文本的實際字節卻不是。 所以我看看如何更改對內存的讀/寫訪問權限(在Windows上,請注意)並在Windows庫中找到VirtualProtect方法,我用它將內存訪問權限更改為PAGE_EXECUTE_WRITECOPY,我認為這會給任何應用程序有一個指向該地址的指針將能夠至少讀取那里的內容。 但這也沒有解決問題。

簡而言之:

我有一個指針(在C#中)到char數組中的第一個char(在C ++應用程序中分配)並且我試圖將該數組的char讀入C#中的字符串。

編輯:

源標頭如下所示:

struct AllocatorHeader
{
// These bytes are reserved, and their purposes may change.
char _reserved[4];

// A pointer to a destructor mapping that is associated with this object.
DestructorMappingBase* _destructor;

// The size of the object this header is for.
unsigned int _size;

char* _name;
};

_name字段是我試圖在C#中取消引用的字段。

編輯:

截至目前,即使使用下面提供的解決方案,我也無法在托管代碼中取消引用此char *。 因此,我只是在內存映射文件引用的池中創建了char *的副本,並使用指向它的指針。 這有效,這讓我相信這是一個與保護相關的問題。 如果我找到一種方法在某個時候繞過這個,我會回答我自己的問題。 在那之前,這將是我的解決方法。 感謝所有幫助過的人!

在我的簡單測試中,這似乎對我有用:

private static unsafe String MarshalUnsafeCStringToString(IntPtr ptr, Encoding encoding) {
    void *rawPointer = ptr.ToPointer();
    if (rawPointer == null) return "";

    char* unsafeCString = (char*)rawPointer;

    int lengthOfCString = 0;
    while (unsafeCString[lengthOfCString] != '\0') {
        lengthOfCString++;
    }

    // now that we have the length of the string, let's get its size in bytes
    int lengthInBytes = encoding.GetByteCount (unsafeCString, lengthOfCString);
    byte[] asByteArray = new byte[lengthInBytes];

    fixed (byte *ptrByteArray = asByteArray) {
        encoding.GetBytes(unsafeCString, lengthOfCString, ptrByteArray, lengthInBytes);
    }

    // now get the string
    return encoding.GetString(asByteArray);
}

也許有點復雜,但假設你的字符串是NUL終止它應該工作。

您的代碼失敗,因為指針只在擁有它們的進程中有效且有意義。 您將內存映射到不同的進程,並且存在完全不同的虛擬地址空間。 沒有什么說內存將被映射到同一個虛擬地址。

如果您知道兩個進程中的基址,則可以調整指針。 但坦率地說,你的整個方法存在嚴重缺陷。 你真的應該在這里使用一些真正的IPC。 命名管道,套接字,WCF,甚至好的舊Windows消息都足夠了。


在評論中你說:

如果在堆上分配了char數組,我可以將此指針解除引用。 但是當char *初始化為char * a =“Hello world”之類的東西時; 它不起作用。

當然不是。 字符串文字由編譯器靜態分配,並駐留在可執行模塊的地址空間中。 您需要在共享堆中分配一個字符串,並使用strcpy將文字復制到共享字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM