Я не могу говорить о 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;
}
При этом, если вышеперечисленное по-прежнему не работает для PHP, вам может потребоваться обернуть возвращенные SAFEARRAY
/BSTR
внутри VARIANT
, как многие языки сценариев обычно обрабатывают данные COM:
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;
...
}
person
Remy Lebeau
schedule
25.05.2019
MultiByteToWideChar()
), то файл на самом деле не является текстовым файлом, и в этом случае почему бы не вернуть содержимое файла в Вместо этого PHP как массив байтов? - person Remy Lebeau   schedule 25.05.2019filepath
передается какBSTR*
, а не просто какBSTR
?BSTR
уже является указателем, аReadFile()
не изменяетfilepath
, поэтому дополнительная косвенность не нужна (если только PHP не использует ее внутренне, что противоречит правилам управления памятью COM). Кроме того, нет необходимости преобразовыватьfilepath
вstd::wstring
,BSTR
можно использовать как есть везде, где ожидаетсяLPCWSTR
. ИReadFile()
необходимоreturn
немедленно, когда он назначает сообщение об ошибке для*Ofile
(не забудьте сначала очистить успешно полученные ресурсы Win32!). - person Remy Lebeau   schedule 25.05.2019