Php 如何从COM DLL返回包含多个空字符的BSTR

Php 如何从COM DLL返回包含多个空字符的BSTR,php,c++,com,atl,bstr,Php,C++,Com,Atl,Bstr,我正在创建一个COM dll,可以从PHP中读取一个内存映射文件,该文件的大小我已经知道,虽然我读取该文件没有问题,但我无法将其作为BSTR正确返回。当我使用dll时,它只返回空字符之前的字符(本例中为3个字符),我知道文件可以包含多个空字符,这就是为什么我在MultiByteToWideChar函数中指定了大小,但它仍然不起作用 STDMETHODIMP CMemReaderImpl::ReadFile(BSTR*文件路径,BSTR*文件) { if(*filepath==nullptr){

我正在创建一个COM dll,可以从PHP中读取一个内存映射文件,该文件的大小我已经知道,虽然我读取该文件没有问题,但我无法将其作为BSTR正确返回。当我使用dll时,它只返回空字符之前的字符(本例中为3个字符),我知道文件可以包含多个空字符,这就是为什么我在MultiByteToWideChar函数中指定了大小,但它仍然不起作用

STDMETHODIMP CMemReaderImpl::ReadFile(BSTR*文件路径,BSTR*文件)
{
if(*filepath==nullptr){
*Ofile=_com_util::ConvertStringToBSTR(“err”);
}
std::wstring wpath(*filepath,SysStringLen(*filepath));
LPCWSTR lpath=wpath.c_str();
处理hFileMap;
PCHAR lpBuffer=NULL;
hFileMap=OpenFileMapping(
文件\u映射\u所有\u访问,
假,,
lpath
);
if(hFileMap==NULL){
char*err=“ERROR”;
*Ofile=\u com\u util::ConvertStringToBSTR(err);
}
lpBuffer=(PCHAR)MapViewOfFile(
hFileMap,
文件\u映射\u所有\u访问,
0,
0,
浅黄色尺寸
);
if(lpBuffer==NULL){
char*err=“ERROR”;
*Ofile=\u com\u util::ConvertStringToBSTR(err);
}
//魔法发生在哪里
int wslen=MultiByteToWideChar(CP_ACP,0,lpBuffer,1000,0,0);
BSTR BSTR=SysAllocStringLen(0,wslen);
MultiByteToWideChar(CP_ACP,0,lpBuffer,1000,bstr,wslen);
*Ofile=bstr;
解编文件(lpBuffer);
CloseHandle(hFileMap);
返回S_OK;
}
我真的希望以BSTR*的形式返回整个文件,这样它就可以被另一个php程序操作,但到目前为止似乎什么都不起作用

php代码:


我不能说PHP,但在COM中,
BSTR
不是传递二进制数据的正确类型,请改用
SAFEARRAY(VT_UI1)

STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, SAFEARRAY** Ofile)
{
    if (!Ofile)
        return E_POINTER;
    *Ofile = nullptr;

    if (!filepath)
        return E_INVALIDARG;

    HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ, FALSE, filepath);
    if (!hFileMap) {
        DWORD err = GetLastError();
        return HRESULT_FROM_WIN32(err);
    }

    LPBYTE lpBuffer = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ 0, 0, BUFF_SIZE);
    if (!lpBuffer) {
        DWORD err = GetLastError();
        CloseHandle(hFileMap);
        return HRESULT_FROM_WIN32(err);
    }

    SAFEARRRAYBOUND bounds;
    bounds.lLbound = 0;
    bounds.cElements = BUFF_SIZE;

    SAFEARRAY *sa = SafeArrayCreate(VT_UI1, 1, &bounds);
    if (!sa) {
        UnmapViewOfFile(lpBuffer);
        CloseHandle(hFileMap);
        return E_OUTOFMEMORY;
    }

    void *data;
    SafeArrayAccessData(sa, &data); 
    memcpy(data, lpBuffer, BUFF_SIZE);
    SafeArrayUnaccessData(sa);

    UnmapViewOfFile(lpBuffer);
    CloseHandle(hFileMap);

    *Ofile = sa;
    return S_OK;
}
不过,我不知道这是否与PHP兼容

如果必须使用
BSTR
,请尝试
SysAllocStringByteLen()
按原样存储字节,而不转换为Unicode:

STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, BSTR* Ofile)
{
    if (!Ofile)
        return E_POINTER;
    *Ofile = nullptr;

    if (!filepath)
        return E_INVALIDARG;

    HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ, FALSE, filepath);
    if (!hFileMap) {
        DWORD err = GetLastError();
        return HRESULT_FROM_WIN32(err);
    }

    LPSTR lpBuffer = (LPSTR) MapViewOfFile(hFileMap, FILE_MAP_READ 0, 0, BUFF_SIZE);
    if (!lpBuffer) {
        DWORD err = GetLastError();
        CloseHandle(hFileMap);
        return HRESULT_FROM_WIN32(err);
    }

    BSTR bstr = SysAllocStringByteLen(lpBuffer, BUFF_SIZE);
    if (bstr) {
        UnmapViewOfFile(lpBuffer);
        CloseHandle(hFileMap);
        return E_OUTOFMEMORY;
    }

    UnmapViewOfFile(lpBuffer);
    CloseHandle(hFileMap);

    *Ofile = bstr;
    return S_OK;
}
如果这对PHP不起作用,请不要对二进制数据使用
MultiByteToWideChar(CP_ACP)
,因为
CP_ACP
将损坏数据!代码页28591(ISO-8859-1)是避免损坏的更好选择,因为在ISO-8859-1中编码的字节与它们表示的Unicode代码点具有相同的数值:

STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, BSTR* Ofile)
{
    if (!Ofile)
        return E_POINTER;
    *Ofile = nullptr;

    if (!filepath)
        return E_INVALIDARG;

    HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ, FALSE, filepath);
    if (!hFileMap) {
        DWORD err = GetLastError();
        return HRESULT_FROM_WIN32(err);
    }

    LPSTR lpBuffer = (LPSTR) MapViewOfFile(hFileMap, FILE_MAP_READ 0, 0, BUFF_SIZE);
    if (!lpBuffer) {
        DWORD err = GetLastError();
        CloseHandle(hFileMap);
        return HRESULT_FROM_WIN32(err);
    }

    int wslen = MultiByteToWideChar(28591, 0, lpBuffer, BUFF_SIZE, nullptr, 0);
    if (wslen == 0) {
        DWORD err = GetLastError();
        UnmapViewOfFile(lpBuffer);
        CloseHandle(hFileMap);
        return HRESULT_FROM_WIN32(err);
    }

    BSTR bstr = SysAllocStringLen(nullptr, wslen);
    if (bstr) {
        UnmapViewOfFile(lpBuffer);
        CloseHandle(hFileMap);
        return E_OUTOFMEMORY;
    }

    MultiByteToWideChar(28591, 0, lpBuffer, BUFF_SIZE, bstr, wslen);

    UnmapViewOfFile(lpBuffer);
    CloseHandle(hFileMap);

    *Ofile = bstr;
    return S_OK;
}
否则,您只需手动将每个8位字节升级为16位字符:

STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, BSTR* Ofile)
{
    if (!Ofile)
        return E_POINTER;
    *Ofile = nullptr;

    if (!filepath)
        return E_INVALIDARG;

    HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ, FALSE, filepath);
    if (!hFileMap) {
        DWORD err = GetLastError();
        return HRESULT_FROM_WIN32(err);
    }

    LPBYTE lpBuffer = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ 0, 0, BUFF_SIZE);
    if (!lpBuffer) {
        DWORD err = GetLastError();
        CloseHandle(hFileMap);
        return HRESULT_FROM_WIN32(err);
    }

    BSTR bstr = SysAllocStringLen(nullptr, BUFF_SIZE);
    if (!bstr) {
        UnmapViewOfFile(lpBuffer);
        CloseHandle(hFileMap);
        return E_OUTOFMEMORY;
    }

    for (int i = 0; i < BUFF_SIZE; ++i)
        bstr[i] = (OLECHAR) lpBuffer[i];

    UnmapViewOfFile(lpBuffer);
    CloseHandle(hFileMap);

    *Ofile = bstr;
    return S_OK;
}

我更好奇的是OP是如何确定只返回第一个null的字符的(即,它们是否使用打印终止字符串的函数)。这是一个疯狂的想法。如何调试代码来检查wslen实际上是什么。那么,wslen的值是多少?是512吗?你有没有检查过bstr的内容(通过内存调试器,你可以看到0个字符)?@SimonMourier感谢你的快速响应,调试代码(在另一个文件中)我可以看到wslen是1000,但bstr只是“II*”前三个字符,我知道还有其他字符,因为如果我单独打印这些字符,我可以看到它们的内容,可能我错过了一个选项。预计为1000。那你怎么看待bstr呢?当你说“其他角色在这里”时,有什么问题?我怀疑在方法方面一切都正常(bstr是1000*2字节的缓冲区,里面有空值)。你确定不是在php端或者在转换过程中(不管是什么),你在空字符后“丢失”字符吗?@SimonMourier也许你是对的问题可能在php端,无论如何,我将编辑我的问题以包含php代码(如4行)。嘿,将safearray包装在一个变体中成功了,我终于可以将数据传递给php,但它是一个字节数组,具有无符号表示。我想现在我必须将数组转换为字符串,但这可能需要很多时间。我希望通过一种快速的方式从C++传递文件到PHP,虽然内存MPEPE文件可以是答案,但是我需要回到TCP套接字传递数据。非常感谢你的帮助
STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, VARIANT* Ofile)
{
    ...
    VariantInit(*Ofile);
    V_VT(*Ofile) = VT_UI1 | VT_ARRAY;
    V_ARRAY(*Ofile) = sa;
    ...
}
STDMETHODIMP CMemReaderImpl::ReadFile(BSTR filepath, VARIANT* Ofile)
{
    ...
    VariantInit(*Ofile);
    V_VT(*Ofile) = VT_BSTR;
    V_BSTR(*Ofile) = bstr;
    ...
}