Конвертация String^ с юникод символами в char*

309
20 февраля 2018, 03:57

И снова проблема со строками (юникод).

Как известно в c# объект System.String содержит unicode строку. При передаче параметров строк в c++-cli, мне необходимо перекодировать данную строку в мульти-байтовую кодировку, без потери данных. Я просто не понимаю как сделать это правильно дабы открыть в libvlc файл который содержит unicode символы как в пути, так и в названии файла?

При вызове Marshal::StringToHGlobalAnsi(file_location); все символы не поддерживающиеся char* передаются как вопросы. При этом libvlc отдает ошибку как unknown error, а в консоли видно что он пытается открыть файл как c:/path/to/???.mp3

void vlc_media::open_file(String ^ file_location) {
    release_media();
    const char* vlc_error;
    IntPtr tmpPtr = Marshal::StringToHGlobalAnsi(file_location);
    char* file_loc = reinterpret_cast<char*>(tmpPtr.ToPointer());
    libvlc_media_t* tmpmptr = libvlc_media_new_path(&m_instance_, file_loc);
    if(!tmpmptr)
    {
        vlc_error = libvlc_errmsg();
        Marshal::FreeHGlobal(tmpPtr);
        throw gcnew Exception(gcnew String(vlc_error));
    }

    m_media_ = IntPtr(tmpmptr);
    Marshal::FreeHGlobal(tmpPtr);
}
Answer 1

Я надеюсь, что VLC умеет принимать широкие строки. (Это скорее всего так, поскольку файловая система у Windows юникодная.) Вам понядобятся заголовки для маршаллирования.

#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
using namespace msclr::interop;
using namespace System;

В этом случае ваш код будет выглядеть так:

String^ managed = L"привет мир";
std::wstring utf16 = marshal_as<std::wstring>(managed);

Если вам нужно utf-8, то тоже несложно:

String^ managed = L"привет мир";
array<Byte>^ managedBytesUtf8 = Text::Encoding::UTF8->GetBytes(managed);
pin_ptr<Byte> nativeBytesUtf8 = &managedBytesUtf8[0];
std::string utf8((char*)nativeBytesUtf8, managedBytesUtf8->Length);
Answer 2

Прибегнул к такому решению, работает на ура:

#pragma once
#include <Windows.h>
using namespace System;
namespace yami {
    namespace vlc {
        ref class vlc_string {
        private:
            char* m_value;
            HRESULT __fastcall UnicodeToAnsi(LPCOLESTR pszW, LPSTR* ppszA);
        public:
            vlc_string(String^ string);
            ~vlc_string();
            static char* operator &(vlc_string^ vlc_str);
        };
    }
}
#include "stdafx.h"
#include "vlc_string.h"
namespace yami {
    namespace vlc {
        HRESULT vlc_string::UnicodeToAnsi(LPCOLESTR pszW, LPSTR * ppszA) {
            ULONG cbAnsi, cCharacters;
            DWORD dwError;
            if (pszW == NULL)
            {
                *ppszA = NULL;
                return NOERROR;
            }
            cCharacters = wcslen(pszW) + 1;
            cbAnsi = cCharacters * 2;
            *ppszA = reinterpret_cast<char*>(malloc(cbAnsi));
            if (NULL == *ppszA)
                return E_OUTOFMEMORY;
            if (0 == WideCharToMultiByte(CP_UTF8, 0, pszW, cCharacters, *ppszA,
                cbAnsi, NULL, NULL))
            {
                dwError = GetLastError();
                free(*ppszA);
                *ppszA = NULL;
                return HRESULT_FROM_WIN32(dwError);
            }
            return NOERROR;
        }
        vlc_string::vlc_string(String^ string) : m_value(nullptr) {
            IntPtr tmpPtr = System::Runtime::InteropServices::Marshal::StringToHGlobalUni(string);
            wchar_t* buffer = reinterpret_cast<wchar_t*>(tmpPtr.ToPointer());
            char* tmp;
            HRESULT result = UnicodeToAnsi(buffer, &tmp);
            if(FAILED(result))
            {
                throw gcnew System::Runtime::InteropServices::COMException("Failed to convert String to vlc_string");
            }
            m_value = new char[strlen(tmp)];
            strcpy(m_value, tmp);
            free(tmp); // чуть не забыл освободить.
            System::Runtime::InteropServices::Marshal::FreeHGlobal(tmpPtr);
        }
        char* vlc_string::operator&(vlc_string ^ value) {
            // используем reinterpret_cast т.к. в классе лежит interop_ptr, а не нативный указатель
            return reinterpret_cast<char*>(value->m_value);
        }
        vlc_string::~vlc_string() {
            if(m_value)
                free(m_value);
        }
    }
}

Использование:

void vlc_media::open_file(String ^ file_location) {
    release_media();
    const char* vlc_error;
    vlc_string^ path = gcnew vlc_string(file_location);
    libvlc_media_t* tmpmptr = libvlc_media_new_path(&m_instance_, &path);
    if (!tmpmptr)
    {
        vlc_error = libvlc_errmsg();
        delete path;
        throw gcnew Exception(gcnew String(vlc_error));
    }
    m_media_ = IntPtr(tmpmptr);
    delete path;
}
READ ALSO
Объясните как работает код

Объясните как работает код

Объясните как работает код программы, и как работает unit-тестЧто откуда берется или вызывается

321
Как добраться через реестр в отдельную папку рандомно

Как добраться через реестр в отдельную папку рандомно

Имеется раздел реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Server\Activate\Random030s\Key - Папка Radom030s ( всегда меняется)

256
Для каких целей сделан управляемый Overlapped Class?

Для каких целей сделан управляемый Overlapped Class?

В каких случаях применяется SystemThreading

242
IPAddress Control проблема отображения

IPAddress Control проблема отображения

Нашёл на просторах интернета следующий лаконичный код для кастомного контрола для ввода IP адресовВсё работает замечательно, но есть одно...

348