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