如何 return 包含来自 COM DLL 的多个空字符的 BSTR
How to return a BSTR which contains multiple null characters from a COM DLL
我正在创建一个 COM dll,可以从 PHP 中使用它来读取我已知其大小的内存映射文件,虽然我读取文件没有问题,但我不能 return 它正确地作为 BSTR。当我使用 dll 时,它只 returns 空字符之前的字符(在本例中为 3 个字符),我知道文件可以包含多个空字符,这就是为什么我在 MultiByteToWideChar 函数中指定了大小,但它仍然不起作用。
STDMETHODIMP CMemReaderImpl::ReadFile(BSTR* filepath, BSTR* Ofile)
{
if (*filepath == nullptr) {
*Ofile = _com_util::ConvertStringToBSTR("err");
}
std::wstring wpath(*filepath, SysStringLen(*filepath));
LPCWSTR lpath = wpath.c_str();
HANDLE hFileMap;
PCHAR lpBuffer = NULL;
hFileMap = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
lpath
);
if (hFileMap == NULL) {
char* err = "ERROR";
*Ofile = _com_util::ConvertStringToBSTR(err);
}
lpBuffer = (PCHAR)MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
BUFF_SIZE
);
if (lpBuffer == NULL) {
char* err = "ERROR";
*Ofile = _com_util::ConvertStringToBSTR(err);
}
//where the magic happens
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;
UnmapViewOfFile(lpBuffer);
CloseHandle(hFileMap);
return S_OK;
}
我真的希望 return 将整个文件作为 BSTR* 以便它可以被另一个 php 程序操作,但到目前为止似乎没有任何效果。
php代码:
<?php
$obj = new COM("MemReader.MemReader");
$result = $obj->ReadFile("Local\imagen3.file");
echo $result; //reads first 3 characters fine
echo $result[4]; //error nothing here
?>
我不能代表 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()
存储字节 as-is 而无需任何转换为 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 位字节 as-is 手动提升为 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;
...
}
我正在创建一个 COM dll,可以从 PHP 中使用它来读取我已知其大小的内存映射文件,虽然我读取文件没有问题,但我不能 return 它正确地作为 BSTR。当我使用 dll 时,它只 returns 空字符之前的字符(在本例中为 3 个字符),我知道文件可以包含多个空字符,这就是为什么我在 MultiByteToWideChar 函数中指定了大小,但它仍然不起作用。
STDMETHODIMP CMemReaderImpl::ReadFile(BSTR* filepath, BSTR* Ofile)
{
if (*filepath == nullptr) {
*Ofile = _com_util::ConvertStringToBSTR("err");
}
std::wstring wpath(*filepath, SysStringLen(*filepath));
LPCWSTR lpath = wpath.c_str();
HANDLE hFileMap;
PCHAR lpBuffer = NULL;
hFileMap = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
lpath
);
if (hFileMap == NULL) {
char* err = "ERROR";
*Ofile = _com_util::ConvertStringToBSTR(err);
}
lpBuffer = (PCHAR)MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
BUFF_SIZE
);
if (lpBuffer == NULL) {
char* err = "ERROR";
*Ofile = _com_util::ConvertStringToBSTR(err);
}
//where the magic happens
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;
UnmapViewOfFile(lpBuffer);
CloseHandle(hFileMap);
return S_OK;
}
我真的希望 return 将整个文件作为 BSTR* 以便它可以被另一个 php 程序操作,但到目前为止似乎没有任何效果。
php代码:
<?php
$obj = new COM("MemReader.MemReader");
$result = $obj->ReadFile("Local\imagen3.file");
echo $result; //reads first 3 characters fine
echo $result[4]; //error nothing here
?>
我不能代表 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()
存储字节 as-is 而无需任何转换为 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 位字节 as-is 手动提升为 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;
...
}