Send mail with Mapi with reply-to multiple emails - c++

I'm working with MAPI library in C++ to send emails. Now I need that the emails I send have a reply-to set to more than one email and I just can do it to one email.
I have been reading that to be able to do this I need to work with the objects FLATENTRYLIST (link) and FLATENTRY (link).
My doubt is how can I store more than one FLATENTRY object in the FLATENTRYLIST. My experience in C++ is not very high so if anyone can help me I will apreciate.
Thanks in advance
Paulo

The FLATENTRYLIST has cEntries member that determines the number of the entries in the list.
You just need to store the entries in abEntries array.

Here is the solution I developed - http://www.codeproject.com/KB/IP/CMapiEx.aspx?msg=3959770#xx3959770xx
Thanks Dmitry for your help.
When I was copying the EntryID to the FlatEntry I as copying the wrong bytes number. Here is the final code that works like a charm.
BOOL CMAPIMessage::SetReplyTo(LPADRLIST lpAddrList)
{
HRESULT hRes = S_OK;
SPropValue pspvReply[1];
LPFLATENTRYLIST lpEntryList = NULL;
BOOL bResult = false;
CString m_strReplyToNames;
int cb = 0;
int displayNameTagID = -1;
int emailTagID = -1;
int entryIDTagID = -1;
int cbAllBytes = 0;
//Get all the EntryID's bytes to initalize the FLATENTRYLIST
for(unsigned j=0; j<lpAddrList->cEntries; j++)
{
for (unsigned i = 0; i < lpAddrList->aEntries[j].cValues; i++)
{
if (lpAddrList->aEntries[j].rgPropVals[i].ulPropTag == PR_ENTRYID)
{
entryIDTagID = i;
}
}
if (entryIDTagID >= 0)
{
int feBytes = CbNewFLATENTRY(lpAddrList->aEntries[j].rgPropVals[entryIDTagID].Value.bin.cb);
cbAllBytes += feBytes;
cbAllBytes += ((feBytes + 4 & ~3) - feBytes);
}
}
//Allocate a new FLATENTRYLIST with all flatentries bytes
cb = CbNewFLATENTRYLIST(cbAllBytes);
hRes = MAPIAllocateBuffer(cb, (LPVOID *)&lpEntryList);
if (FAILED(hRes))
{
bResult = false;
goto Cleanup;
}
ZeroMemory((VOID *)lpEntryList, cb);
// Copy the bits of the FLATENTRY into the FLATENTRYLIST.
lpEntryList->cEntries = lpAddrList->cEntries;
int countBytesAdded = 0;
for(unsigned j=0; j<lpAddrList->cEntries; j++)
{
for (unsigned i = 0; i < lpAddrList->aEntries[j].cValues; i++)
{
if (lpAddrList->aEntries[j].rgPropVals[i].ulPropTag == PR_TRANSMITABLE_DISPLAY_NAME)
{
displayNameTagID = i;
}
else if (lpAddrList->aEntries[j].rgPropVals[i].ulPropTag == PR_EMAIL_ADDRESS)
{
emailTagID = i;
}
else if (lpAddrList->aEntries[j].rgPropVals[i].ulPropTag == PR_ENTRYID)
{
entryIDTagID = i;
}
}
if ((emailTagID>=0) && (entryIDTagID>=0))
{
CString m_strReplyToName;
CString m_strReplyToEmail;
m_strReplyToEmail=lpAddrList->aEntries[j].rgPropVals[emailTagID].Value.lpszA;
if(displayNameTagID>=0)
m_strReplyToName=lpAddrList->aEntries[j].rgPropVals[displayNameTagID].Value.lpszA;
else
m_strReplyToName = m_strReplyToEmail;
m_strReplyToNames += (CString)m_strReplyToName + ";";
// Allocate a new FLATENTRY structure for the PR_REPLY_RECIPIENT_ENTRIES property
LPFLATENTRY lpReplyEntry = NULL;
cb = CbNewFLATENTRY(lpAddrList->aEntries[j].rgPropVals[entryIDTagID].Value.bin.cb);
hRes = MAPIAllocateBuffer(cb, (LPVOID *)&lpReplyEntry);
if (FAILED(hRes))
{
bResult = false;
goto Cleanup;
}
ZeroMemory((VOID *)lpReplyEntry, cb);
// Copy the bits of the entry id into the FLATENTRY structure
CopyMemory(lpReplyEntry->abEntry,
lpAddrList->aEntries[j].rgPropVals[entryIDTagID].Value.bin.lpb,
lpAddrList->aEntries[j].rgPropVals[entryIDTagID].Value.bin.cb);
lpReplyEntry->cb = lpAddrList->aEntries[j].rgPropVals[entryIDTagID].Value.bin.cb;
int missingBytes = 0;
int feBytes = CbFLATENTRY(lpReplyEntry);
missingBytes = ((feBytes + 4 & ~3) - feBytes);
//Copy each FLATENTRY to the abEntries, next to the previous added bytes
CopyMemory(lpEntryList->abEntries + countBytesAdded, lpReplyEntry, feBytes);
countBytesAdded += feBytes + missingBytes;
//Clean Memory
if (lpReplyEntry) MAPIFreeBuffer(lpReplyEntry);
}
displayNameTagID = -1;
emailTagID = -1;
entryIDTagID = -1;
}
lpEntryList->cbEntries = countBytesAdded;
pspvReply[0].ulPropTag = PR_REPLY_RECIPIENT_ENTRIES;
// Allocate memory in the lpb to hold the FLATENTRYLIST
hRes = MAPIAllocateBuffer(CbFLATENTRYLIST(lpEntryList), (LPVOID *)&pspvReply[0].Value.bin.lpb);
if (FAILED(hRes))
{
bResult = false;
goto Cleanup;
}
// Copy the memory into the SPropValue
CopyMemory(pspvReply[0].Value.bin.lpb,lpEntryList,CbFLATENTRYLIST(lpEntryList));
pspvReply[0].Value.bin.cb = CbFLATENTRYLIST(lpEntryList);
//Set the PR_REPLY_RECIPIENT_NAMES property with all names
SetPropertyString(PR_REPLY_RECIPIENT_NAMES, m_strReplyToNames);
// Set the property that contains the FLATENTRYLIST with the reply-to adresses to the message properties
hRes = Message()->SetProps(1,pspvReply,NULL);
if (FAILED(hRes))
{
bResult = false;
goto Cleanup;
}
bResult = true;
Cleanup:
if (lpEntryList) MAPIFreeBuffer(lpEntryList);
return bResult;
}

Related

Why am I reading garbage data or getting acces violations?

I'm using Windows Web Services to access a WCF service and it creates a an array of pointers of the results.
I had a similar issue where I was also reading garbage data and I though it might be caused by sendign this data to another function that was causing the pointers to get invalidated but now I'm storing it in a vector reference to try to avoid such issues but the problem persists. I'm not sure what do do anymore.
I also tried using ListaDeMaterialesBE** lstmat = new ListaDeMaterialesBE*; but that did not work at all.
Here is the code:
void svc_listMaterials(const size_t& idProject, std::vector<ListaDeMaterialesBE>& result) {
HRESULT hr = ERROR_SUCCESS;
WS_ERROR* error = NULL;
WS_HEAP* heap = NULL;
WS_SERVICE_PROXY* proxy = NULL;
WS_ENDPOINT_ADDRESS address = {};
const WS_STRING url = WS_STRING_VALUE(L"http://localhost/SIMSE_Service.ServicioEstadistico.svc");
address.url = url;
WS_HTTP_BINDING_TEMPLATE templateValue = {};
ULONG maxMessageSize = 2147483647;
WS_CHANNEL_PROPERTY channelProperty [ 1 ];
channelProperty [ 0 ].id = WS_CHANNEL_PROPERTY_MAX_BUFFERED_MESSAGE_SIZE;
channelProperty [ 0 ].value = &maxMessageSize;
channelProperty [ 0 ].valueSize = sizeof(maxMessageSize);
WS_CHANNEL_PROPERTIES channelProperties;
channelProperties.properties = channelProperty;
channelProperties.propertyCount = 1;
templateValue.channelProperties = channelProperties;
hr = WsCreateError(NULL, 0, &error);
if (FAILED(hr)) {
}
hr = WsCreateHeap(2048, 512, NULL, 0, &heap, error);
WS_HTTP_BINDING_TEMPLATE templ = {};
hr = BasicHttpBinding_IServicioEstadistico_CreateServiceProxy(&templateValue, NULL, 0, &proxy, error);
hr = WsOpenServiceProxy(proxy, &address, NULL, error);
// The issue starts here
ListaDeMaterialesBE** lstmat;
unsigned int rcount;
hr = BasicHttpBinding_IServicioEstadistico_ListarMaterialesPorProyecto(proxy,
idProject,
&rcount,
&lstmat,
heap,
NULL,
NULL,
NULL,
error);
// Here is where we seem to be reading garbage data
if (rcount > 1 && rcount < 10000) {
for (size_t i = 1; i <= rcount; ++i) {
ListaDeMaterialesBE t = **lstmat;
result.push_back(t);
lstmat++;
}
}
if (proxy) {
WsCloseServiceProxy(proxy, NULL, NULL);
WsFreeServiceProxy(proxy);
}
if (heap) {
WsFreeHeap(heap);
}
if (error) {
WsFreeError(error);
}
if (FAILED(hr)) {
std::cout << "errored \n";
}
}
I think the reason for the error is your wrong use of the returned secondary pointer.
I created a simple example to simulate the problem:
#define COUNT 10
void f(int** pp)
{
*pp = new int[COUNT];
for (int i = 0; i < COUNT; i++)
{
(*pp)[i] = i;
}
}
int main(int argc, const char* argv[])
{
int** pp = new int*;
f(pp);
for (int i = 0; i < COUNT; i++)
{
cout << **pp;
pp++;
}
return 0;
}
The function f obtains a secondary pointer, this secondary pointer saves the first address of an array, and then writes it.
Then use your method to traverse the array, it will cause an access exception:
The reason for the error is that when you use the secondary pointer to perform an auto-increment operation, the distance it moves each time is not the distance of an element you think.
So you should get the first address of the array it saves through the secondary pointer, and then traverse:
int** pp = new int*;
f(pp);
auto p = *pp;
for (int i = 0; i < COUNT; i++)
{
cout << *p;
p++;
}
Then we can read the correct data:

Reading SAFEARRAY of UDT containing SAFEARRAY

I'm trying to create and read a SAFEARRAY(MyUDT)* where MyUDT contains a SAFEARRAY, but when I try to read it I got an "Access violation" exception.
I defined the following struct and enum:
typedef [uuid(...)]
enum CollisionDetectorMoveType
{
CollisionDetectorMoveType_Unknown = -1,
CollisionDetectorMoveType_Move = 0,
CollisionDetectorMoveType_Measure
} CollisionDetectorMoveType;
typedef [uuid(...)]
struct CollisionDetectorXYZ
{
double X;
double Y;
double Z;
} CollisionDetectorXYZ;
typedef [uuid(...)]
struct CollisionDetectorMultiPosition
{
SAFEARRAY(CollisionDetectorXYZ) pos;
CollisionDetectorMoveType type;
} CollisionDetectorMultiPosition;
bool createTestCollisionDetectorMultiPositionSafeArray(SAFEARRAY** psa)
{
IRecordInfoPtr recordset_info = nullptr;
HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorMultiPosition, &recordset_info);
if (FAILED(hr))
{
return false;
}
SAFEARRAYBOUND safearray_bound;
memset(&safearray_bound, 0, sizeof(safearray_bound));
safearray_bound.cElements = 10;
safearray_bound.lLbound = 0;
*psa = ::SafeArrayCreateEx(VT_RECORD, 1, &safearray_bound, (PVOID)recordset_info);
for (size_t i = 0; i < 10; ++i)
{
CollisionDetectorMultiPosition multiPosition;
multiPosition.type = i % 2 == 0 ? CollisionDetectorMoveType_Move : CollisionDetectorMoveType_Measure;
if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
{
return false;
}
LONG current_position = i;
::SafeArrayPutElement(*psa, &current_position, &multiPosition);
}
::SafeArrayUnaccessData(*psa);
return true;
}
}
bool createTestCollisionDetectorXYZSafeArray(SAFEARRAY** psa)
{
IRecordInfoPtr recordset_info = nullptr;
HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorXYZ, &recordset_info);
if (FAILED(hr))
{
return false;
}
SAFEARRAYBOUND safearray_bound;
memset(&safearray_bound, 0, sizeof(safearray_bound));
safearray_bound.cElements = 10;
safearray_bound.lLbound = 0;
*psa = ::SafeArrayCreateEx(VT_RECORD, 1, &safearray_bound, (PVOID)recordset_info);
for (size_t i = 0; i < 10; ++i)
{
CollisionDetectorXYZ current_point;
current_point.X = i;
current_point.Y = i;
current_point.Z = i;
LONG current_position = i;
::SafeArrayPutElement(*psa, &current_position, &current_point);
}
::SafeArrayUnaccessData(*psa);
return true;
}
Then I try to create and read the SAFEARRAY:
SAFEARRAY* psa2 = NULL;
if (!createTestCollisionDetectorMultiPositionSafeArray(&psa2))
{
*retVal = SYSERR;
return S_OK;
}
CollisionDetectorMultiPosition* current_pointer_to_element;
auto access_result = ::SafeArrayAccessData(psa2, (void **)&current_pointer_to_element);
if (FAILED(access_result))
{
return S_OK;
}
LONG upper_bound, lower_bound;
::SafeArrayGetLBound(psa2, 1, &lower_bound);
::SafeArrayGetUBound(psa2, 1, &upper_bound);
std::vector<CollisionDetectorMultiPosition> multiPositionVector;
LONG size = upper_bound - lower_bound + 1;
for (LONG i = 0; i < size; i++)
{
CollisionDetectorMultiPosition this_value;
::SafeArrayGetElement(psa2, &i, (void *)&this_value);
multiPositionVector.push_back(this_value);
}
However when I call ::SafeArrayGetElement(psa2, &i, (void *)&this_value); I got the exception.
What I'm doing wrong?
If I simply call
CollisionDetectorMultiPosition multiPosition;
multiPosition.type = CollisionDetectorMoveType_Move;
if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
{
*retVal = SYSERR;
return S_OK;
}
I can read the multiPosition.pos SAFEARRAY without any problem.
Thanks for your help.
Solved
I found a solution, I report the code for all the users which could have the same problem. Basically the trick is to create a SAFEARRAY of VARIANT, and fill the VARIANT struct with the CollisionDetectorMultiPosition.
CComVariant variant;
variant.vt = VT_RECORD;
variant.pvRecord = &multiPosition;
variant.pRecInfo = recordset_info;
Creation of SAFEARRAY
bool createTestCollisionDetectorMultiPositionSafeArray(SAFEARRAY** psa)
{
IRecordInfoPtr recordset_info;
HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorMultiPosition, &recordset_info);
if (FAILED(hr))
{
return false;
}
SAFEARRAYBOUND safearray_bound;
memset(&safearray_bound, 0, sizeof(safearray_bound));
safearray_bound.cElements = 10;
safearray_bound.lLbound = 0;
*psa = SafeArrayCreate(VT_VARIANT, 1, &safearray_bound);
for (size_t i = 0; i < 10; ++i)
{
CollisionDetectorMultiPosition multiPosition;
multiPosition.type = i % 2 == 0 ? CollisionDetectorMoveType_Move : CollisionDetectorMoveType_Measure;
if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
{
return false;
}
CComVariant variant;
variant.vt = VT_RECORD;
variant.pvRecord = &multiPosition;
variant.pRecInfo = recordset_info;
LONG current_position = i;
::SafeArrayPutElement(*psa, &current_position, &variant);
variant.vt = VT_EMPTY;
}
::SafeArrayUnaccessData(*psa);
return true;
}
Reading the SAFEARRAY
SAFEARRAY* psa2 = NULL;
if (!createTestCollisionDetectorMultiPositionSafeArray(&psa2))
{
*retVal = AC3SYSERR;
return S_OK;
}
LONG upper_bound, lower_bound;
::SafeArrayGetLBound(psa2, 1, &lower_bound);
::SafeArrayGetUBound(psa2, 1, &upper_bound);
std::vector<std::pair<CollisionDetectorMoveType, std::vector<CollisionDetectorXYZ>>> multiPositionVector;
LONG size = upper_bound - lower_bound + 1;
for (LONG i = 0; i < size; i++)
{
VARIANT this_value;
VariantInit(&this_value);
VariantClear(&this_value);
::SafeArrayGetElement(psa2, &i, (void *)&this_value);
CollisionDetectorMultiPosition* this_value2 = (CollisionDetectorMultiPosition *)this_value.pvRecord;
CollisionDetectorXYZ* current_pointer_to_element;
auto access_result = ::SafeArrayAccessData(this_value2->pos, (void **)&current_pointer_to_element);
if (FAILED(access_result))
{
return S_OK;
}
LONG upper_bound, lower_bound;
::SafeArrayGetLBound(this_value2->pos, 1, &lower_bound);
::SafeArrayGetUBound(this_value2->pos, 1, &upper_bound);
std::vector<CollisionDetectorXYZ> moves;
LONG size = upper_bound - lower_bound + 1;
for (LONG i = 0; i < size; i++)
{
CollisionDetectorXYZ this_value;
::SafeArrayGetElement(this_value2->pos, &i, (void *)&this_value);
moves.push_back(this_value);
}
multiPositionVector.push_back(std::make_pair(this_value2->type, moves));
::SafeArrayUnaccessData(this_value2->pos);
}
Actually I had similar problem and this thread helped me a lot, but there are some details specific to MFC ActiveX that took me a couple of days to demystify and I want to share them with the community.
My objective was to add a method in an MFC/ATL ActiveX that would return an Array of UDT to COM enabled programming languages.
My first problem was exposing the UUID of the UDT to the C++ code; for some reason despite giving a GUID to the UDT it was not being generated nor becoming available to C++. I found that I had to add cpp_quote in IDL to force it.
[
uuid(GUID_LIBRARY),
version(VERSION)
...
]
library WebKitXCEF3Lib
{
importlib(STDOLE_TLB);
typedef [public, uuid(<your guid here>)] struct Cookie
{
BSTR Name;
BSTR Domain;
BSTR Path;
BSTR Value;
VARIANT_BOOL HttpOnly;
VARIANT_BOOL Secure;
VARIANT_BOOL Expires;
VARIANT ExpiryDateUTC;
} Cookie;
cpp_quote("struct __declspec(uuid(\"{<your guid here>}\")) Cookie;")
...
}
Next, the return type of the method returning the UDT Array has to be VARIANT:
[id(100)] VARIANT GetCookies(BSTR Domain);
The DISPATCH MAP should define the return type as VT_VARIANT too:
BEGIN_DISPATCH_MAP(CMyCtrl, COleControl)
DISP_FUNCTION_ID(CMyCtrl, "GetCookies", dispidGetCookies, GetCookies, VT_VARIANT, VTS_BSTR)
END_DISPATCH_MAP()
And finally the C++ code must create a SAFEARRAY using SafeArrayCreateEx and provide the IRecordInfo of the UDT, which finally wraps to this:
VARIANT CMyCtrl::GetCookies(LPCTSTR Domain)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
SAFEARRAY* psaCookies = nullptr;
if(AmbientUserMode() && !Unloading || BROWSER_READY)
{
LONG L = Cookies.size();
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = L;
IRecordInfoPtr pRecInfo = nullptr; // DO NOT RELEASE !!!
if(SUCCEEDED(GetRecordInfoFromGuids(LIBID_MyLib, VERSION_MAJOR, VERSION_MINOR, 0, __uuidof(Cookie), &pRecInfo)))
{
psaCookies = ::SafeArrayCreateEx(VT_RECORD, 1, rgsabound, pRecInfo);
if(psaCookies)
{
Cookie* pCookies = NULL;
if(SUCCEEDED(SafeArrayAccessData(psaCookies, reinterpret_cast<PVOID*>(&pCookies))))
{
for(LONG i = 0; i < L; i++)
{
pCookies[i].Name = WSTR_to_BSTR(Cookies[i].Name);
pCookies[i].Domain = WSTR_to_BSTR(Cookies[i].Domain);
pCookies[i].Path = WSTR_to_BSTR(Cookies[i].Path);
pCookies[i].Value = WSTR_to_BSTR(Cookies[i].Value);
pCookies[i].HttpOnly = Cookies[i].HttpOnly;
pCookies[i].Secure = Cookies[i].Secure;
pCookies[i].Expires = Cookies[i].Expires;
VariantCopy(&(pCookies[i].ExpiryDateUTC), &(Cookies[i].ExpiryDateUTC));
}
SafeArrayUnaccessData(psaCookies);
}
}
}
}
VARIANT result;
VariantInit(&result);
if(psaCookies)
{
result.vt = VT_ARRAY | VT_RECORD;
result.parray = psaCookies;
}
return result;
}
Be careful NOT to release the IRecordInfo and the returning VARIANT must be of type VT_ARRAY | VT_RECORD.
The VB6 code looks like this:
Private Sub wxDesigner_OnPageComplete(ByVal URL As String)
Dim Cookies As Variant
Cookies = wxDesigner.GetCookies("foo.com")
End Sub
And in Watches the VARIANT looks like this:

rapid TS fragment ffmpeg decoding - memory leak

Environment:
Ubuntu 16.04 (x64)
C++
ffmpeg
Use-case
Multiple MPEG-TS fragments are rapidly decoded ( numerous every sec )
The format of the TS fragments is dynamic and can't be known ahead of time
The first A/V frames of each fragment are needed to be extracted
Problem statement
The code bellow successfully decodes A/V, BUT, has a huge memory leak ( MBytes/sec )
According to the docs seems all memory is freed as it should ( does it... ? )
Why do I get this huge mem leak, what am I missing in the following code snap ?
struct MEDIA_TYPE {
ffmpeg::AVMediaType eType;
union {
struct {
ffmpeg::AVPixelFormat colorspace;
int width, height;
float fFPS;
} video;
struct : WAVEFORMATEX {
short sSampleFormat;
} audio;
} format;
};
struct FRAME {
enum { MAX_PALNES = 3 + 1 };
int iStrmId;
int64_t pts; // Duration in 90Khz clock resolution
uint8_t** ppData; // Null terminated
int32_t* pStride;// Zero terminated
};
HRESULT ProcessTS(IN Operation op, IN uint8_t* pTS, IN uint32_t uiBytes, bool(*cb)(IN const MEDIA_TYPE& mt, IN FRAME& frame, IN PVOID pCtx), IN PVOID pCbCtx)
{
uiBytes -= uiBytes % 188;// align to 188 packet size
struct CONTEXT {
uint8_t* pTS;
uint32_t uiBytes;
int32_t iPos;
} ctx = { pTS, uiBytes, 0 };
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this);
ffmpeg::AVFormatContext* pFmtCtx = 0;
if (0 == (pFmtCtx = ffmpeg::avformat_alloc_context()))
return E_OUTOFMEMORY;
ffmpeg::AVIOContext* pIoCtx = ffmpeg::avio_alloc_context(pTS, uiBytes, 0, &ctx
, [](void *opaque, uint8_t *buf, int buf_size)->int {
auto pCtx = (CONTEXT*)opaque;
int size = pCtx->uiBytes;
if (pCtx->uiBytes - pCtx->iPos < buf_size)
size = pCtx->uiBytes - pCtx->iPos;
if (size > 0) {
memcpy(buf, pCtx->pTS + pCtx->iPos, size);
pCtx->iPos += size;
}
return size;
}
, 0
, [](void* opaque, int64_t offset, int whence)->int64_t {
auto pCtx = (CONTEXT*)opaque;
switch (whence)
{
case SEEK_SET:
pCtx->iPos = offset;
break;
case SEEK_CUR:
pCtx->iPos += offset;
break;
case SEEK_END:
pCtx->iPos = pCtx->uiBytes - offset;
break;
case AVSEEK_SIZE:
return pCtx->uiBytes;
}
return pCtx->iPos;
});
pFmtCtx->pb = pIoCtx;
int iRet = ffmpeg::avformat_open_input(&pFmtCtx, "fakevideo.ts", m_pInputFmt, 0);
if (ERROR_SUCCESS != iRet) {
assert(false);
pFmtCtx = 0;// a user-supplied AVFormatContext will be freed on failure.
return E_FAIL;
}
struct DecodeContext {
ffmpeg::AVStream* pStream;
ffmpeg::AVCodec* pDecoder;
int iFramesProcessed;
};
HRESULT hr = S_OK;
int iStreamsProcessed = 0;
bool bVideoFound = false;
int64_t ptsLast = 0;
int64_t dtsLast = 0;
auto pContext = (DecodeContext*)alloca(sizeof(DecodeContext) * pFmtCtx->nb_streams);
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++) {
assert(pFmtCtx->streams[i]->index == i);
pContext[i].pStream = pFmtCtx->streams[i];
pContext[i].pDecoder = ffmpeg::avcodec_find_decoder(pFmtCtx->streams[i]->codec->codec_id);
pContext[i].iFramesProcessed= 0;
if (0 == pContext[i].pDecoder)
continue;
if ((iRet = ffmpeg::avcodec_open2(pFmtCtx->streams[i]->codec, pContext[i].pDecoder, NULL)) < 0) {
_ASSERT(FALSE);
hr = E_FAIL;
goto ErrExit;
}
}
while (S_OK == hr) {
ffmpeg::AVFrame* pFrame = 0;
ffmpeg::AVPacket pkt;
ffmpeg::av_init_packet(&pkt);
if (ERROR_SUCCESS != (iRet = ffmpeg::av_read_frame(pFmtCtx, &pkt))) {
hr = E_FAIL;
break;
}
if ((0 == dtsLast) && (0 != pkt.dts))
dtsLast = pkt.dts;
if ((0 == ptsLast) && (0 != pkt.pts))
ptsLast = pkt.pts;
DecodeContext& ctx = pContext[pkt.stream_index];
if (Operation::DECODE_FIRST_FRAME_OF_EACH_STREAM == op) {
if (iStreamsProcessed == pFmtCtx->nb_streams) {
hr = S_FALSE;
goto Next;
}
if (ctx.iFramesProcessed > 0)
goto Next;
iStreamsProcessed++;
}
if (0 == ctx.pDecoder)
goto Next;
if (0 == (pFrame = ffmpeg::av_frame_alloc())) {
hr = E_OUTOFMEMORY;
goto Next;
}
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x, decode, S:%d, T:%d\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this, pkt.stream_index, ctx.pStream->codec->codec_type);
int bGotFrame = false;
int iBytesUsed = 0;
MEDIA_TYPE mt;
memset(&mt, 0, sizeof(mt));
mt.eType = ctx.pStream->codec->codec_type;
switch (mt.eType) {
case ffmpeg::AVMediaType::AVMEDIA_TYPE_AUDIO:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
// FFMPEG AAC decoder oddity, first call to 'avcodec_decode_audio4' results mute audio where the second result the expected audio
bGotFrame = false;
if ((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (false == bGotFrame)
goto Next;
iBytesUsed = ctx.pStream->codec->frame_size;
mt.format.audio.nChannels = ctx.pStream->codec->channels;
mt.format.audio.nSamplesPerSec = ctx.pStream->codec->sample_rate;
mt.format.audio.wBitsPerSample = ffmpeg::av_get_bytes_per_sample(ctx.pStream->codec->sample_fmt) * 8;
mt.format.audio.nBlockAlign = mt.format.audio.nChannels * mt.format.audio.wBitsPerSample / 8;
mt.format.audio.sSampleFormat = (short)pFrame->format;
break;
case ffmpeg::AVMediaType::AVMEDIA_TYPE_VIDEO:
if ((iRet = ffmpeg::avcodec_decode_video2(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
break;
}
if (false == bGotFrame)
goto Next;
assert(ffmpeg::AVPixelFormat::AV_PIX_FMT_YUV420P == ctx.pStream->codec->pix_fmt);// Thats is the only color space currently supported
iBytesUsed = (ctx.pStream->codec->width * ctx.pStream->codec->height * 3) / 2;
mt.format.video.width = ctx.pStream->codec->width;
mt.format.video.height = ctx.pStream->codec->height;
mt.format.video.colorspace = ctx.pStream->codec->pix_fmt;
mt.format.video.fFPS = (float)ctx.pStream->codec->framerate.num / ctx.pStream->codec->framerate.den;
bVideoFound = true;
break;
default:
goto Next;
}
ctx.iFramesProcessed++;
{
FRAME f = { ctx.pStream->index, ((0 == ptsLast) ? dtsLast : ptsLast), (uint8_t**)pFrame->data, (int32_t*)pFrame->linesize };
if ((iRet > 0) && (false == cb(mt, f, pCbCtx)))
hr = S_FALSE;// Breaks the loop
}
Next:
ffmpeg::av_free_packet(&pkt);
if (0 != pFrame) {
//ffmpeg::av_frame_unref(pFrame);
ffmpeg::av_frame_free(&pFrame);
pFrame = 0;
}
}
ErrExit:
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++)
ffmpeg::avcodec_close(pFmtCtx->streams[i]->codec);
pIoCtx->buffer = 0;// We have allocated the buffer, no need for ffmpeg to free it 4 us
pFmtCtx->pb = 0;
ffmpeg::av_free(pIoCtx);
ffmpeg::avformat_close_input(&pFmtCtx);
ffmpeg::avformat_free_context(pFmtCtx);
return hr;
}
You need to unref the packets before reusing them. And there's no need to allocate and deallocate them all the time.
Here's how I do it which might help you:
// Initialise a packet queue
std::list<AVPacket *> packets;
...
for (int c = 0; c < MAX_PACKETS; c++) {
ff->packets.push_back(av_packet_alloc());
}
while (!quit) {
... get packet from queue
int err = av_read_frame(ff->context, packet);
... process packet (audio, video, etc)
av_packet_unref(packet); // add back to queue for reuse
}
// Release packets
while (ff->packets.size()) { // free packets
AVPacket *packet = ff->packets.front();
av_packet_free(&packet);
ff->packets.pop_front();
}
In your code you've freed a packet which wasn't allocated in the first place.

GetLogicalDriveStrings() and char - Where am I doing wrongly

I want to search a file which may be present in any drives such as C:\, D:\ etc. Using GetLogicalDriveStrings I can able to get the list of drives but when I add anything extra for the output, I am getting a null in the output prompt. Here is my code:
#include "StdAfx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// Buffer length
DWORD mydrives = 100;
// Buffer for drive string storage
char lpBuffer[100];
const char *extFile = "text.ext";
// You may want to try the wmain() version
int main(void)
{
DWORD test;
int i;
test = GetLogicalDriveStrings(mydrives, (LPWSTR)lpBuffer);
if(test != 0)
{
printf("GetLogicalDriveStrings() return value: %d, Error (if any): %d \n", test, GetLastError());
printf("The logical drives of this machine are:\n");
// Check up to 100 drives...
for(i = 0; i<100; i++)
printf("%c%s", lpBuffer[i],extFile);
printf("\n");
}
else
printf("GetLogicalDriveStrings() is failed lor!!! Error code: %d\n", GetLastError());
_getch();
return 0;
}
I want above output as C:\text.ext D:\text.ext ... rather I am getting text.ext only. I am using Microsoft Visual C++ 2010 Express
GetLogicalDriveStrings() returns a double-null terminated list of null-terminated strings. E.g., say you had drives A, B and C in your machine. The returned string would look like this:
A:\<nul>B:\<nul>C:\<nul><nul>
You can use the following code to iterate through the strings in the returned buffer and print each one in turn:
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH] = {0};
DWORD dwResult = GetLogicalDriveStrings(dwSize,szLogicalDrives);
if (dwResult > 0 && dwResult <= MAX_PATH)
{
char* szSingleDrive = szLogicalDrives;
while(*szSingleDrive)
{
printf("Drive: %s\n", szSingleDrive);
// get the next drive
szSingleDrive += strlen(szSingleDrive) + 1;
}
}
Note that the details of how the function works, including the example code that I shamelessly copied and pasted, can be found by reading the docs.
Did you mean to put the printf in the loop?
Currently, you set extFile 100 times (just to be sure?!)
for(i = 0; i<100; i++)
extFile = "text.ext";
You meant to show all the drive letters in a loop:
for(i = 0; i<100; i++)
{
extFile = "text.ext";
printf("%c%s", lpBuffer[i], extFile); //I guess you mean extFile here?
}
DWORD dwSize = MAX_PATH;
WCHAR szLogicalDrives[MAX_PATH] = { 0 };
DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
CStringArray m_Drives;
m_Drives.RemoveAll();
if (dwResult > 0 && dwResult <= MAX_PATH)
{
WCHAR* szSingleDrive = szLogicalDrives;
while (*szSingleDrive)
{
UINT nDriveType = GetDriveType(szSingleDrive);
m_Drives.Add(CString(szSingleDrive, 2));
// get the next drive
szSingleDrive += wcslen(szSingleDrive) + 1;
}
}
return m_Drives;
class DriveList {
protected:
LPTSTR m_driveList;
DWORD m_driveCount;
DWORD m_bufSize = 32 * sizeof(TCHAR);
public:
virtual ~DriveList() {
free(m_driveList);
}
DriveList() {
m_driveList = (LPTSTR)malloc(m_bufSize);
}
int getDriveCount() const {
return m_driveCount;
}
TCHAR operator[] (const int index) const {
return m_driveList[index];
}
void loadDriveList() {
DWORD mask;
if((mask = GetLogicalDrives()) == 0) {
throw;
}
m_driveCount = 0;
for(int x = 0; x <= 25; x++ ) {
if(mask & 1) {
m_driveList[m_driveCount] = TCHAR(65 + x);
m_driveCount += 1;
}
mask >>= 1;
}
}
};

How to get Serial Port profile SDP record for remote device

I am trying to write an application that pairs a remote bluetooth device with Windows Mobile/CE handheld and get the service of the device.
I noticed that when you manually pair a device and set the service through the system(eg. go to settings->Bluetooth-> add device) it generates an sdprecord value in the following registry
HKLM/Software/Microsoft/Bluetooth/Device/{deviceAddress}/{*Service_UUID*}.
I am basically trying to implement this in my program.
I followed the following documentations:
http://msdn.microsoft.com/en-us/library/ms883458.aspx
http://msdn.microsoft.com/en-us/library/ms883398.aspx
and then convert the SDPrecord into binary and write it to the registry:
RegSetValueEx(hSerialPortKey,_T("sdprecord"),0,REG_BINARY,(BYTE*)&pRecord,sizeof(pRecord)*2+1);
The result is not the same as what I would get when I manually pair the device.
What am I doing wrong? How can I retrieve the SDP record? Thank you in advance.
Below is my code
BTHNS_RESTRICTIONBLOB RBlob;
memset(&RBlob,0,sizeof(BTHNS_RESTRICTIONBLOB));
RBlob.type = SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST;
RBlob.numRange = 1;
RBlob.uuids[0].uuidType= SDP_ST_UUID16;//SDP_ST_UUID128;
RBlob.uuids[0].u.uuid16= SerialPortServiceClassID_UUID16;
RBlob.pRange[0].minAttribute =SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST;
RBlob.pRange[0].maxAttribute = SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST;
SOCKADDR_BTH sa;
memset (&sa, 0, sizeof(SOCKADDR_BTH));
BTH_ADDR *pDeviceAddr = &devAddress;
*(BTH_ADDR *)(&sa.btAddr) = *pDeviceAddr;
sa.addressFamily = AF_BTH;
CSADDR_INFO csai;
memset(&csai,0,sizeof(CSADDR_INFO));
csai.RemoteAddr.lpSockaddr = (sockaddr*)&sa;
csai.RemoteAddr.iSockaddrLength = sizeof(sa);
BLOB blob;
blob.cbSize = sizeof(BTHNS_RESTRICTIONBLOB);
blob.pBlobData = (BYTE *)&RBlob;
WSAQUERYSET wsaq;
memset(&wsaq,0,sizeof(WSAQUERYSET));
wsaq.dwSize = sizeof(WSAQUERYSET);
wsaq.dwNumberOfCsAddrs = 1;
wsaq.dwNameSpace = NS_BTH;
wsaq.lpBlob = &blob;
wsaq.lpcsaBuffer = &csai;
wsaq.lpServiceClassId = &serialPortUUID;
//Initialising winsock
WSADATA data;
int result = WSAStartup(MAKEWORD(2, 2), &data);//initializing winsock
if (hLib == NULL)
{
return S_FALSE;
}
result = WSALookupServiceBegin (&wsaq,0, &hLookup);
while (result ==ERROR_SUCCESS )
{
BYTE sdpBuffer[4096];
memset(sdpBuffer,0,sizeof(sdpBuffer));
LPWSAQUERYSET pResult = (LPWSAQUERYSET)&sdpBuffer;
dwSize = sizeof(sdpBuffer);
pResult->dwSize = sizeof(WSAQUERYSET);
pResult->dwNameSpace = NS_BTH;
pResult->lpBlob = NULL;
pResult->lpServiceClassId = &serialPortUUID;
result = WSALookupServiceNext(hLookup,0,&dwSize,pResult);
if(result == -1 )
{
int error;
error = WSAGetLastError();
if (error == WSA_E_NO_MORE)
break;
}
else
{
//Get SDP record
if (pResult != NULL)
{
if (ERROR_SUCCESS != ServiceAndAttributeSearchParse(pResult->lpBlob->pBlobData,pResult->lpBlob->cbSize,&pRecordArg,&ulRecords)) { //error handling}
ULONG recordIndex;
for (recordIndex = 0; recordIndex < ulRecords; recordIndex++) {
pRecord = pRecordArg[recordIndex];}
I think the code should be ok.Here's my code
int FindRFCOMMChannel (unsigned char *pStream, int cStream, unsigned char *pChann)
{
ISdpRecord **pRecordArg;
int cRecordArg = 0;
*pChann = 0;
int hr = ServiceAndAttributeSearch(pStream, cStream, &pRecordArg, (ULONG *)&cRecordArg);
if (hr != ERROR_SUCCESS)
{
BT_ERROR( CBTRFCOMMMgr, PerformServiceSearch, "ServiceAndAttributeSearch ERROR %d\n");
return hr;
}
for (int i = 0; (! *pChann) && (i < cRecordArg); i++)
{
ISdpRecord *pRecord = pRecordArg[i]; // particular record to examine in this loop
CNodeDataFreeString protocolList; // contains SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST data, if available
if (ERROR_SUCCESS != pRecord->GetAttribute(SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST,&protocolList) ||
(protocolList.type != SDP_TYPE_CONTAINER))
continue;
ISdpNodeContainer *pRecordContainer = protocolList.u.container;
int cProtocols = 0;
NodeData protocolDescriptor; // information about a specific protocol (i.e. L2CAP, RFCOMM, ...)
pRecordContainer->GetNodeCount((DWORD *)&cProtocols);
for (int j = 0; (! *pChann) && (j < cProtocols); j++) {
pRecordContainer->GetNode(j,&protocolDescriptor);
if (protocolDescriptor.type != SDP_TYPE_CONTAINER)
continue;
ISdpNodeContainer *pProtocolContainer = protocolDescriptor.u.container;
int cProtocolAtoms = 0;
pProtocolContainer->GetNodeCount((DWORD *)&cProtocolAtoms);
for (int k = 0; (! *pChann) && (k < cProtocolAtoms); k++) {
NodeData nodeAtom; // individual data element, such as what protocol this is or RFCOMM channel id.
pProtocolContainer->GetNode(k,&nodeAtom);
if (IsRfcommUuid(&nodeAtom)) {
if (k+1 == cProtocolAtoms) {
// misformatted response. Channel ID should follow RFCOMM uuid
break;
}
NodeData channelID;
pProtocolContainer->GetNode(k+1,&channelID);
*pChann = (unsigned char)GetChannel(&channelID);
break; // formatting error
}
}
}
}
for (i = 0; i < cRecordArg; i++)
pRecordArg[i]->Release();
CoTaskMemFree(pRecordArg);
return (*pChann != 0) ? 1:0;
}
int IsRfcommUuid(NodeData *pNode) {
if (pNode->type != SDP_TYPE_UUID)
return FALSE;
if (pNode->specificType == SDP_ST_UUID16)
return (pNode->u.uuid16 == RFCOMM_PROTOCOL_UUID16);
else if (pNode->specificType == SDP_ST_UUID32)
return (pNode->u.uuid32 == RFCOMM_PROTOCOL_UUID16);
else if (pNode->specificType == SDP_ST_UUID128)
return (0 == memcmp(&RFCOMM_PROTOCOL_UUID,&pNode->u.uuid128,sizeof(GUID)));
return FALSE;
}
int GetChannel (NodeData *pChannelNode) {
if (pChannelNode->specificType == SDP_ST_UINT8)
return pChannelNode->u.uint8;
else if (pChannelNode->specificType == SDP_ST_INT8)
return pChannelNode->u.int8;
else if (pChannelNode->specificType == SDP_ST_UINT16)
return pChannelNode->u.uint16;
else if (pChannelNode->specificType == SDP_ST_INT16)
return pChannelNode->u.int16;
else if (pChannelNode->specificType == SDP_ST_UINT32)
return pChannelNode->u.uint32;
else if (pChannelNode->specificType == SDP_ST_INT32)
return pChannelNode->u.int32;
return 0;
}