windows-nt/Source/XPSP1/NT/multimedia/media/sndrec32/soundrec/oleglue.c
2020-09-26 16:20:57 +08:00

789 lines
19 KiB
C

/* (C) Copyright Microsoft Corporation 1991-1994. All Rights Reserved */
//
// FILE: oleglue.c
//
// NOTES: OLE-related outbound references from SoundRecorder
//
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <shellapi.h>
#include <objbase.h>
#define INCLUDE_OLESTUBS
#include "soundrec.h"
#include "srecids.h"
//
// GLOBALS
//
// should unify state variables and put globals into a single location
DWORD dwOleBuildVersion = 0; // OLE library version number
BOOL gfOleInitialized = FALSE; // did OleInitialize succeed?
BOOL gfStandalone = FALSE; // status, are we a non-embedded object
BOOL gfEmbedded = FALSE; // were we invoked with an -Embedding flag?
BOOL gfLinked = FALSE; // are we a linked object?
BOOL gfTerminating = FALSE; // has TerminateServer been called?
BOOL gfHideAfterPlaying = FALSE;
BOOL gfShowWhilePlaying = TRUE;
BOOL gfCloseAtEndOfPlay = FALSE;
TCHAR gachLinkFilename[_MAX_PATH];
BOOL gfClosing = FALSE;
int giExtWidth; // Metafile extent width
int giExtHeight; // Metafile extent height
//
// Utility functions ported from old OLE1 code
//
/*
* DibFromBitmap()
*
* Will create a global memory block in DIB format that represents the DDB
* passed in
*
*/
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
HANDLE FAR PASCAL DibFromBitmap(HBITMAP hbm, HPALETTE hpal, HANDLE hMem)
{
BITMAP bm;
BITMAPINFOHEADER bi;
BITMAPINFOHEADER FAR *lpbi;
DWORD dw;
HANDLE hdib = NULL;
HDC hdc;
HPALETTE hpalT;
if (!hbm)
return NULL;
GetObject(hbm,sizeof(bm),&bm);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = (bm.bmPlanes * bm.bmBitsPixel) > 8 ? 24 : 8;
bi.biCompression = BI_RGB;
bi.biSizeImage = (DWORD)WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = bi.biBitCount == 8 ? 256 : 0;
bi.biClrImportant = 0;
dw = bi.biSize + bi.biClrUsed * sizeof(RGBQUAD) + bi.biSizeImage;
if (hMem && GlobalSize(hMem) != 0)
{
if (GlobalSize(hMem) < dw)
return NULL;
lpbi = GlobalLock(hMem);
}
else
lpbi = GlobalAllocPtr(GHND | GMEM_DDESHARE, dw);
if (!lpbi)
return NULL;
*lpbi = bi;
hdc = CreateCompatibleDC(NULL);
if (hdc)
{
if (hpal)
{
hpalT = SelectPalette(hdc,hpal,FALSE);
RealizePalette(hdc);
}
GetDIBits(hdc, hbm, 0, (UINT)bi.biHeight,
(LPBYTE)lpbi + (int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD),
(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
if (hpal)
SelectPalette(hdc,hpalT,FALSE);
DeleteDC(hdc);
}
hdib = GlobalHandle(lpbi);
GlobalUnlock(hdib);
return hdib;
}
HANDLE GetDIB(HANDLE hMem)
{
HPALETTE hpal = GetStockObject(DEFAULT_PALETTE);
HBITMAP hbm = GetBitmap();
HANDLE hDib = NULL;
if (hbm && hpal)
{
hDib = DibFromBitmap(hbm,hpal,hMem);
if (!hDib)
DOUT(TEXT("DibFromBitmap failed!\r\n"));
}
if (hpal)
DeleteObject(hpal);
if (hbm)
DeleteObject(hbm);
return hDib;
}
HBITMAP
GetBitmap(void)
{
HDC hdcmem = NULL;
HDC hdc = NULL;
HBITMAP hbitmap = NULL;
HBITMAP holdbitmap = NULL;
RECT rc;
hdc = GetDC(ghwndApp);
if (hdc)
{
SetRect(&rc, 0, 0,
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
hdcmem = CreateCompatibleDC(hdc);
if (hdcmem)
{
hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
holdbitmap = (HBITMAP)SelectObject(hdcmem, hbitmap);
// paint directly into the bitmap
PatBlt(hdcmem, 0, 0, rc.right, rc.bottom, WHITENESS);
DrawIcon(hdcmem, 0, 0, ghiconApp);
hbitmap = (HBITMAP)SelectObject(hdcmem, holdbitmap);
DeleteDC(hdcmem);
}
ReleaseDC(ghwndApp, hdc);
}
return hbitmap;
}
#pragma message("this code should extract the picture from the file")
HANDLE
GetPicture(void)
{
HANDLE hpict = NULL;
HMETAFILE hMF = NULL;
LPMETAFILEPICT lppict = NULL;
HBITMAP hbmT = NULL;
HDC hdcmem = NULL;
HDC hdc = NULL;
BITMAP bm;
HBITMAP hbm;
hbm = GetBitmap();
if (hbm == NULL)
return NULL;
GetObject(hbm, sizeof(bm), (LPVOID)&bm);
hdc = GetDC(ghwndApp);
if (hdc)
{
hdcmem = CreateCompatibleDC(hdc);
ReleaseDC(ghwndApp, hdc);
}
if (!hdcmem)
{
DeleteObject(hbm);
return NULL;
}
hdc = CreateMetaFile(NULL);
hbmT = (HBITMAP)SelectObject(hdcmem, hbm);
SetWindowOrgEx(hdc, 0, 0, NULL);
SetWindowExtEx(hdc, bm.bmWidth, bm.bmHeight, NULL);
StretchBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight,
hdcmem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
hMF = CloseMetaFile(hdc);
SelectObject(hdcmem, hbmT);
DeleteObject(hbm);
DeleteDC(hdcmem);
lppict = (LPMETAFILEPICT)GlobalAllocPtr(GHND|GMEM_DDESHARE
, sizeof(METAFILEPICT));
if (!lppict)
{
if (hMF)
DeleteMetaFile(hMF);
return NULL;
}
hdc = GetDC(ghwndApp);
lppict->mm = MM_ANISOTROPIC;
lppict->hMF = hMF;
lppict->xExt = MulDiv(bm.bmWidth, 2540, GetDeviceCaps(hdc, LOGPIXELSX));
lppict->yExt = MulDiv(bm.bmHeight, 2540, GetDeviceCaps(hdc, LOGPIXELSX));
giExtWidth = lppict->xExt;
giExtHeight = lppict->yExt;
ReleaseDC(ghwndApp, hdc);
hpict = GlobalHandle(lppict);
GlobalUnlock(hpict);
return hpict;
}
//
// Code ported from server.c (OLE1) for serialization...
//
HANDLE GetNativeData(void)
{
LPBYTE lplink = NULL;
MMIOINFO mmioinfo;
HMMIO hmmio;
BOOL fOk;
lplink = (LPBYTE)GlobalAllocPtr(GHND | GMEM_SHARE, 4096L);
if (lplink == NULL)
{
#if DBG
OutputDebugString(TEXT("GetNativeData: malloc failed\r\n"));
#endif
return NULL;
}
mmioinfo.fccIOProc = FOURCC_MEM;
mmioinfo.pIOProc = NULL;
mmioinfo.pchBuffer = lplink;
mmioinfo.cchBuffer = 4096L; // initial size
mmioinfo.adwInfo[0] = 4096L; // grow by this much
hmmio = mmioOpen(NULL, &mmioinfo, MMIO_READWRITE);
if (hmmio == NULL)
{
GlobalFreePtr(lplink);
return NULL;
}
fOk = WriteWaveFile(hmmio
, gpWaveFormat
, gcbWaveFormat
, gpWaveSamples
, glWaveSamplesValid);
mmioGetInfo(hmmio, &mmioinfo, 0);
mmioClose(hmmio,0);
if (fOk)
{
//
// Warning, the buffer we allocated may have been realloc'd
//
HANDLE hlink = GlobalHandle(mmioinfo.pchBuffer);
GlobalUnlock(hlink);
return hlink;
}
else
{
gfErrorBox++;
ErrorResBox( ghwndApp
, ghInst
, MB_ICONEXCLAMATION | MB_OK
, IDS_APPTITLE
, IDS_ERROREMBED
);
#if DBG
OutputDebugString(TEXT("Failed to WriteWaveFile\r\n"));
#endif
gfErrorBox--;
//
// Warning, the buffer we allocated may have been realloc'd
//
if (mmioinfo.pchBuffer)
GlobalFreePtr(mmioinfo.pchBuffer);
return NULL;
}
}
/*
* Called from OLE storage code
*/
LPBYTE PutNativeData(LPBYTE lpbData, DWORD dwSize)
{
MMIOINFO mmioinfo;
HMMIO hmmio;
MMRESULT mmr;
LPWAVEFORMATEX pwfx;
DWORD cbwfx;
DWORD cbdata;
LPBYTE pdata;
mmioinfo.fccIOProc = FOURCC_MEM;
mmioinfo.pIOProc = NULL;
mmioinfo.pchBuffer = lpbData;
mmioinfo.cchBuffer = dwSize; // initial size
mmioinfo.adwInfo[0] = 0L; // grow by this much
hmmio = mmioOpen(NULL, &mmioinfo, MMIO_READ);
if (hmmio)
{
mmr = ReadWaveFile(hmmio
, &pwfx
, &cbwfx
, &pdata
, &cbdata
, TEXT("NativeData")
, TRUE);
mmioClose(hmmio,0);
if (mmr != MMSYSERR_NOERROR)
return NULL;
if (pwfx == NULL)
return NULL;
DestroyWave();
gpWaveFormat = pwfx;
gcbWaveFormat = cbwfx;
gpWaveSamples = pdata;
glWaveSamples = cbdata;
}
//
// update state variables
//
glWaveSamplesValid = glWaveSamples;
glWavePosition = 0L;
gfDirty = FALSE;
//
// update the display
//
UpdateDisplay(TRUE);
return (LPBYTE)gpWaveSamples;
}
/*
* PERMANENT ENTRY POINTS
*/
BOOL ParseCommandLine(LPTSTR lpCommandLine);
/*
* modifies gfEmbedded, initializes gStartParams
*/
BOOL InitializeSRS(HINSTANCE hInst)
{
TCHAR * lptCmdLine = GetCommandLine();
BOOL fOLE = FALSE, fServer;
gfUserClose = FALSE;
gachLinkFilename[0] = 0;
fServer = ParseCommandLine(lptCmdLine);
gfEmbedded = fServer; // We are embedded or linked
if (!fServer)
{
if (gStartParams.achOpenFilename[0] != 0)
{
lstrcpy(gachLinkFilename, gStartParams.achOpenFilename);
}
}
//
// Only if we are invoked as an embedded object do we initialize OLE.
// Defer initialization for the standalone object until later.
//
if (gfEmbedded)
fOLE = InitializeOle(hInst);
return fOLE;
}
/* OLE initialization
*/
BOOL InitializeOle(HINSTANCE hInst)
{
BOOL fOLE;
DOUT(TEXT("SOUNDREC: Initializing OLE\r\n"));
dwOleBuildVersion = OleBuildVersion();
// Fix bug #33271
// As stated in the docs:
// Typically, the COM library is initialized on an apartment only once.
// Subsequent calls will succeed, as long as they do not attempt to change
// the concurrency model of the apartment, but will return S_FALSE. To close
// the COM library gracefully, each successful call to OleInitialize,
// including those that return S_FALSE, must be balanced by a corresponding
// call to OleUninitialize.
// gfOleInitialized = (OleInitialize(NULL) == NOERROR) ? TRUE : FALSE;
gfOleInitialized = SUCCEEDED(OleInitialize(NULL));
if (gfOleInitialized)
fOLE = CreateSRClassFactory(hInst, gfEmbedded);
else
fOLE = FALSE; // signal a serious problem!
return fOLE;
}
/*
* Initialize the state of the application or
* change state from Embedded to Standalone.
*/
void FlagEmbeddedObject(BOOL flag)
{
// Set global state variables. Note, gfEmbedding is untouched.
gfEmbeddedObject = flag;
gfStandalone = !flag;
}
void SetOleCaption(
LPTSTR lpszObj)
{
TCHAR aszFormatString[256];
LPTSTR lpszTitle;
//
// Change title to "Sound Object in XXX"
//
LoadString(ghInst, IDS_OBJECTTITLE, aszFormatString,
SIZEOF(aszFormatString));
lpszTitle = (LPTSTR)GlobalAllocPtr(GHND, (lstrlen(lpszObj) + SIZEOF(aszFormatString))*sizeof(TCHAR));
if (lpszTitle)
{
wsprintf(lpszTitle, aszFormatString, lpszObj);
SetWindowText(ghwndApp, lpszTitle);
GlobalFreePtr(lpszTitle);
}
}
void SetOleMenu(
HMENU hMenu,
LPTSTR lpszObj)
{
TCHAR aszFormatString[256];
LPTSTR lpszMenu;
//
// Change menu to "Exit & Return to XXX"
//
LoadString(ghInst, IDS_EXITANDRETURN, aszFormatString,
SIZEOF(aszFormatString));
lpszMenu = (LPTSTR)GlobalAllocPtr(GHND, (lstrlen(lpszObj) + SIZEOF(aszFormatString))*sizeof(TCHAR));
if (lpszMenu)
{
wsprintf(lpszMenu, aszFormatString, lpszObj);
ModifyMenu(hMenu, IDM_EXIT, MF_BYCOMMAND, IDM_EXIT, lpszMenu);
GlobalFreePtr(lpszMenu);
}
}
/* Adjust menus according to system state.
* */
void FixMenus(void)
{
HMENU hMenu;
hMenu = GetMenu(ghwndApp);
if (!gfLinked && gfEmbeddedObject)
{
// Remove these menu items as they are irrelevant.
DeleteMenu(hMenu, IDM_NEW, MF_BYCOMMAND);
DeleteMenu(hMenu, IDM_SAVE, MF_BYCOMMAND);
DeleteMenu(hMenu, IDM_SAVEAS, MF_BYCOMMAND);
DeleteMenu(hMenu, IDM_REVERT, MF_BYCOMMAND);
DeleteMenu(hMenu, IDM_OPEN, MF_BYCOMMAND);
}
else
{
TCHAR ach[40];
// Is this necessary?
LoadString(ghInst, IDS_NONEMBEDDEDSAVE, ach, SIZEOF(ach));
ModifyMenu(hMenu, IDM_SAVE, MF_BYCOMMAND, IDM_SAVE, ach);
}
//
// Update the titlebar and exit menu too.
//
if (!gfLinked && gfEmbeddedObject)
{
LPTSTR lpszObj = NULL;
LPTSTR lpszApp = NULL;
OleObjGetHostNames(&lpszApp,&lpszObj);
if (lpszObj)
lpszObj = (LPTSTR)FileName((LPCTSTR)lpszObj);
if (lpszObj)
{
SetOleCaption(lpszObj);
SetOleMenu(hMenu, lpszObj);
}
}
DrawMenuBar(ghwndApp); /* Can't hurt... */
}
#define WM_USER_DESTROY (WM_USER+10)
//
// Called from WM_CLOSE (from user) or SCtrl::~SCtrl (from container)
//
void TerminateServer(void)
{
DOUT(TEXT("SoundRec: TerminateServer\r\n"));
gfTerminating = TRUE;
if (gfOleInitialized)
{
WriteObjectIfEmpty();
ReleaseSRClassFactory();
FlushOleClipboard();
//
// If, at this time, we haven't closed, we really should.
//
if (!gfClosing)
{
DoOleClose(TRUE);
AdviseClosed();
}
}
//
// only if the user is terminating OR we're embedded
//
if (gfUserClose || !gfStandalone)
PostMessage(ghwndApp, WM_USER_DESTROY, 0, 0);
}
/* start params!
* the app will use these params to determine behaviour once started.
*/
StartParams gStartParams = { FALSE,FALSE,FALSE,FALSE,TEXT("") };
BOOL ParseCommandLine(LPTSTR lpCommandLine)
{
#define TEST_STRING_MAX 11 // sizeof szEmbedding
#define NUMOPTIONS 6
static TCHAR szEmbedding[] = TEXT("embedding");
static TCHAR szPlay[] = TEXT("play");
static TCHAR szOpen[] = TEXT("open");
static TCHAR szNew[] = TEXT("new");
static TCHAR szClose[] = TEXT("close");
static struct tagOption {
LPTSTR name;
LPTSTR filename;
int cchfilename;
LPBOOL state;
} options [] = {
{ NULL, gStartParams.achOpenFilename, _MAX_PATH, &gStartParams.fOpen },
{ szEmbedding, gStartParams.achOpenFilename, _MAX_PATH, &gfEmbedded },
{ szPlay, gStartParams.achOpenFilename, _MAX_PATH, &gStartParams.fPlay },
{ szOpen, gStartParams.achOpenFilename, _MAX_PATH, &gStartParams.fOpen },
{ szNew, gStartParams.achOpenFilename, _MAX_PATH, &gStartParams.fNew },
{ szClose, NULL, 0, &gStartParams.fClose }
};
LPTSTR pchNext;
int iOption = 0,i,cNumOptions = sizeof(options)/sizeof(struct tagOption);
TCHAR szSwitch[TEST_STRING_MAX];
TCHAR ch;
if (lpCommandLine == NULL)
return FALSE;
/* skip argv[0] */
if (*lpCommandLine == TEXT('"'))
{
//
// eat up everything to the next quote
//
lpCommandLine++;
do {
ch = *lpCommandLine++;
}
while (ch != TEXT('"'));
}
else
{
//
// eat up everything to the next whitespace
//
ch = *lpCommandLine;
while (ch != TEXT(' ') && ch != TEXT('\t') && ch != TEXT('\0'))
ch = *++lpCommandLine;
}
pchNext = lpCommandLine;
while ( *pchNext )
{
LPTSTR pchName = options[iOption].filename;
int cchName = options[iOption].cchfilename;
/* whitespace */
switch (*pchNext)
{
case TEXT(' '):
case TEXT('\t'):
pchNext++;
continue;
case TEXT('-'):
case TEXT('/'):
{
lstrcpyn(szSwitch,pchNext+1,TEST_STRING_MAX);
szSwitch[TEST_STRING_MAX-1] = 0;
/* scan to the NULL or ' ' and terminate string */
for (i = 0; i < TEST_STRING_MAX && szSwitch[i] != 0; i++)
if (szSwitch[i] == TEXT(' '))
{
szSwitch[i] = 0;
break;
}
/* now test each option switch for a hit */
for (i = 0; i < cNumOptions; i++)
{
if (options[i].name == NULL)
continue;
if (!lstrcmpi(szSwitch,options[i].name))
{
*(options[i].state) = TRUE;
if (options[i].filename)
/* next non switch string applies to this option */
iOption = i;
break;
}
}
/* seek ahead */
while (*pchNext && *pchNext != TEXT(' '))
pchNext++;
continue;
}
case TEXT('\"'):
/* filename */
/* copy up to next quote */
pchNext++;
while (*pchNext && *pchNext != TEXT('\"'))
{
if (cchName)
{
*pchName++ = *pchNext++;
cchName--;
}
else
break;
}
pchNext++;
continue;
default:
/* filename */
/* copy up to the end */
while (*pchNext && cchName)
{
*pchName++ = *pchNext++;
cchName--;
}
break;
}
}
/* special case.
* we are linked if given a LinkFilename and an embedding flag.
* Does this ever happen or only through IPersistFile?
*/
if (gfEmbedded && gStartParams.achOpenFilename[0] != 0)
{
gfLinked = TRUE;
}
return gfEmbedded;
}
void
BuildUniqueLinkName(void)
{
//
//Ensure a unique filename in gachLinkFilename so we can create valid
//FileMonikers...
//
if(gachLinkFilename[0] == 0)
{
TCHAR aszFile[_MAX_PATH];
GetTempFileName(TEXT("."), TEXT("Tmp"), 0, gachLinkFilename);
/* GetTempFileName creates an empty file, delete it.
*/
GetFullPathName(gachLinkFilename,SIZEOF(aszFile),aszFile,NULL);
DeleteFile(aszFile);
}
}
void AppPlay(BOOL fClose)
{
if (fClose)
{
//ugh. don't show while playing.
gfShowWhilePlaying = FALSE;
}
if (IsWindow(ghwndApp))
{
gfCloseAtEndOfPlay = fClose;
PostMessage(ghwndApp,WM_COMMAND,ID_PLAYBTN, 0L);
}
}