Created
November 12, 2014 09:43
-
-
Save seraphy/28917741f60bf4b1496b to your computer and use it in GitHub Desktop.
DDEサーバーの実装例(VC6コンパイル可能バージョン)、メイン画面は、スクロールバーつきログ表示機能あり。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DDEServerExampleVC6.cpp | |
// | |
#include "stdafx.h" | |
// stdafx.h内に、#pragma warning(disable:4786) をいれてmap展開の長さ警告を抑制する | |
#include <windows.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <tchar.h> | |
#include <ddeml.h> | |
#include <memory> | |
#include <vector> | |
#include <list> | |
#include <map> | |
/** | |
* ウインドクラス名 | |
*/ | |
static TCHAR szWindowClass[] = _T("DDESRVWndClass"); | |
/** | |
* サービス名 | |
*/ | |
static LPCTSTR SERVICE_NAME = _T("DDESRV"); | |
/** | |
* アプリケーションタイトル | |
*/ | |
static LPCTSTR TITLE_NAME = _T("DDESRV Sample Application"); | |
/** | |
* インスタンスハンドル | |
*/ | |
HINSTANCE hInst; | |
/** | |
* DDEMLのハンドル | |
*/ | |
DWORD m_idInst; | |
/** | |
* DDEのサービス名 | |
*/ | |
HSZ m_hszServiceName; | |
/** | |
* アドバイズループ中のトピックとアイテムを示す | |
*/ | |
struct Item | |
{ | |
HSZ hszTopic; | |
HSZ hszItem; | |
}; | |
/** | |
* アドバイズループ中のトピックとアイテムのリストを会話ごとに保持するマップ | |
*/ | |
std::map<HCONV, std::list<Item> > itemsMap; | |
/** | |
* ログメッセージデータ | |
*/ | |
struct LogData | |
{ | |
/** | |
* 連番 | |
*/ | |
int number; | |
/** | |
* 時刻 | |
*/ | |
SYSTEMTIME systime; | |
/** | |
* メッセージ | |
*/ | |
std::vector<TCHAR> message; | |
}; | |
/** | |
* ログの通し番号 | |
*/ | |
int logSerialNo = 0; | |
/** | |
* ログの上限 | |
*/ | |
const int maxLogs = 1000; | |
/** | |
* ログを保持するリスト. | |
* 上限を超えると古いものから削除されてゆく。 | |
*/ | |
std::list<LogData> logs; | |
/** | |
* ログを登録する | |
*/ | |
void AppendLog(std::vector<TCHAR> message) | |
{ | |
LogData logData; | |
logData.number = ++logSerialNo; | |
GetLocalTime(&logData.systime); | |
logData.message = message; | |
logs.push_back(logData); | |
while (logs.size() > maxLogs) { | |
logs.pop_front(); | |
} | |
} | |
/** | |
* ログを登録する(書式付) | |
*/ | |
void AppendLog(LPCTSTR message, ...) | |
{ | |
va_list args; | |
va_start(args, message); | |
int len = lstrlen(message); | |
std::vector<TCHAR> buf(len + 512); | |
wvsprintf(&buf[0], message, args); | |
va_end(args); | |
AppendLog(buf); | |
} | |
/** | |
* DDEの文字列ハンドルから文字列を取得する. | |
*/ | |
std::vector<TCHAR> handleToString(HSZ handle) | |
{ | |
std::vector<TCHAR> buf(1); | |
DWORD reqLen = DdeQueryString(m_idInst, handle, NULL, 0, CP_WINNEUTRAL); | |
if (reqLen > 0) { | |
buf.resize(reqLen + 1, 0); | |
LPTSTR pBuf = &buf[0]; | |
DdeQueryString(m_idInst, handle, pBuf, reqLen + 1, CP_WINNEUTRAL); | |
} | |
return buf; | |
} | |
/** | |
* DDEのデータハンドルからバイト列を取得する. | |
*/ | |
std::vector<BYTE> dataHandleToBytes(HDDEDATA hdata) | |
{ | |
//ハンドルからデータのアドレスを得る | |
DWORD reqLen = DdeGetData(hdata, NULL, 0, 0); | |
std::vector<BYTE> buf(reqLen); | |
DdeGetData(hdata, (LPBYTE) &buf[0], reqLen, 0); | |
return buf; | |
} | |
/** | |
* 文字列からバイトデータを作成する. | |
* フォーマットがCF_TEXTの場合はMBCS、CF_UNICODETEXTの場合はUNICODEでバイト列を作成する. | |
*/ | |
HDDEDATA stringToData(LPCTSTR uniBuf, HSZ hszItem, UINT uFmt) | |
{ | |
HDDEDATA data; | |
if (uFmt == CF_TEXT) { | |
#ifdef _UNICODE | |
int len = lstrlen(uniBuf); | |
int bufsiz = len * 2 + 1; | |
std::vector<CHAR> buf(bufsiz); | |
WideCharToMultiByte(CP_ACP, 0, uniBuf, len, &buf[0], bufsiz, NULL, NULL); | |
LPCSTR p = (LPCSTR) &buf[0]; | |
#else | |
LPCSTR p = (LPCSTR) uniBuf; | |
#endif | |
data = DdeCreateDataHandle(m_idInst, | |
(LPBYTE)p, lstrlenA(p) + 1, 0, hszItem, CF_TEXT, 0); | |
} else { | |
#ifdef _UNICODE | |
LPCWSTR p = (LPCWSTR) uniBuf; | |
#else | |
int len = lstrlen(uniBuf); | |
std::vector<WCHAR> buf(len + 1); | |
MultiByteToWideChar(CP_ACP, 0, uniBuf, len, &buf[0], len + 1); | |
LPCWSTR p = (LPCWSTR) &buf[0]; | |
#endif | |
data = DdeCreateDataHandle(m_idInst, | |
(LPBYTE)p, (lstrlenW(p) + 1) * sizeof(WCHAR), 0, hszItem, CF_UNICODETEXT, 0); | |
} | |
return data; | |
} | |
/** | |
* 応答用テキストを作成する. | |
* 単一文字列と、タブ区切り改行による複数行文字列の、いずれかをメニューより切り替え可能とする. | |
* (ExcelのDDERequestは、タブ区切りで列、改行(CR)で行と認識する配列として戻り値を認識する.) | |
* (単一の値の場合でも、一要素の一次元配列となる.) | |
*/ | |
std::vector<TCHAR> GetResponseText(LPCTSTR topic, LPCTSTR item) | |
{ | |
int len1 = lstrlen(topic); | |
int len2 = lstrlen(item); | |
int total = 64 + len1 + len2; | |
std::vector<TCHAR> buf(total); | |
wsprintf(&buf[0], _T("%ld:%s#%s"), GetTickCount(), topic, item); | |
return buf; | |
} | |
/** | |
* DDEMLのコールバック | |
*/ | |
HDDEDATA CALLBACK DdeCallback( | |
UINT uType, | |
UINT uFmt, | |
HCONV hconv, | |
HSZ hszTopic, | |
HSZ hszItem, | |
HDDEDATA hdata, | |
DWORD dwData1, | |
DWORD dwData2) | |
{ | |
// uTypeにメッセージの種類が入っている | |
switch(uType) | |
{ | |
/** | |
* DDEサービスが登録された場合にコールバックされる | |
*/ | |
case XTYP_REGISTER: | |
{ | |
std::vector<TCHAR> serviceName = handleToString(hszTopic); | |
std::vector<TCHAR> instanceName = handleToString(hszItem); | |
AppendLog(_T("XTYP_REGISTER serviceName=%s instance-serviceName=%s"), | |
&serviceName[0], &instanceName[0]); | |
return (HDDEDATA)TRUE; | |
} | |
/** | |
* DDEサービスが登録解除された場合にコールバックされる | |
*/ | |
case XTYP_UNREGISTER: | |
{ | |
std::vector<TCHAR> serviceName = handleToString(hszTopic); | |
std::vector<TCHAR> instanceName = handleToString(hszItem); | |
AppendLog(_T("XTYP_UNREGISTER serviceName=%s instance-serviceName=%s"), | |
&serviceName[0], &instanceName[0]); | |
return (HDDEDATA)TRUE; | |
} | |
/** | |
* クライアントと接続された場合にコールバックされる | |
*/ | |
case XTYP_CONNECT: | |
{ | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
AppendLog(_T("XTYP_CONNECT topicName=%s"), &topicName[0]); | |
// トピック名をチェックし接続可能であればTRUE、そうでなければFALSEを返す. | |
// この時点では接続していないため、hConvは設定されていない | |
return (HDDEDATA)TRUE; | |
} | |
/** | |
* 接続確認の通知 | |
*/ | |
case XTYP_CONNECT_CONFIRM: | |
{ | |
AppendLog(_T("XTYP_CONNECT_CONFIRM conv=%lx"), hconv); | |
return (HDDEDATA)TRUE; | |
} | |
/** | |
* クライアントと接続解除された場合にコールバックされる | |
*/ | |
case XTYP_DISCONNECT: | |
{ | |
AppendLog(_T("XTYP_DISCONNECT conv=%lx"), hconv); | |
// 同一conv内のアドバイズをすべてクリアする | |
itemsMap.erase(hconv); | |
return (HDDEDATA)TRUE; | |
} | |
/** | |
* 直接、指定されたトピックとアイテムのデータを取得する. | |
*/ | |
case XTYP_REQUEST: | |
{ | |
if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) { | |
AppendLog(_T("XTYP_REQUEST reject uFmt=%d"), uFmt); | |
return (HDDEDATA)0; | |
} | |
// hsz2には項目名が入っている。この項目名から要求されているデータを識別する | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
std::vector<TCHAR> itemName = handleToString(hszItem); | |
// 応答文字列の作成 | |
std::vector<TCHAR> uniBuf = GetResponseText(&topicName[0], &itemName[0]); | |
AppendLog(_T("XTYP_REQUEST conv=%lx topicName=%s itemName=%s uFmt=%ld result=%s"), | |
hconv, &topicName[0], &itemName[0], uFmt, &uniBuf[0]); | |
// バイトデータとして返却する | |
return stringToData(&uniBuf[0], hszItem, uFmt); | |
} | |
case XTYP_ADVSTART: | |
{ | |
if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) { | |
// アドバイズループを確立できるフォーマットでなければリジェクトする必要がある。 | |
// (適合するフォーマットがでるまで、クライアント側より繰り返し呼び出される。) | |
AppendLog(_T("XTYP_ADVSTART conv=%lx reject uFmt=%d"), hconv, uFmt); | |
return (HDDEDATA)0; | |
} | |
// アドバイズループの開始 | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
std::vector<TCHAR> itemName = handleToString(hszItem); | |
AppendLog(_T("XTYP_ADVSTART conv=%lx topicName=%s itemName=%s uFmt=%ld"), | |
hconv, &topicName[0], &itemName[0], uFmt); | |
if (lstrcmp(&itemName[0], _T("StdDocumentName")) == 0) { | |
return (HDDEDATA) FALSE; | |
} | |
// アドバイズ対象の記録 | |
std::map<HCONV, std::list<Item> >::iterator ite = itemsMap.find(hconv); | |
if (ite == itemsMap.end()) { | |
ite = itemsMap.insert(std::map<HCONV, std::list<Item> >::value_type(hconv, std::list<Item>())).first; | |
} | |
std::list<Item> &items = ite->second; | |
Item item = {hszTopic, hszItem}; | |
items.push_back(item); | |
return (HDDEDATA) TRUE; | |
} | |
case XTYP_ADVSTOP: | |
{ | |
// アドバイズループの終了 | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
std::vector<TCHAR> itemName = handleToString(hszItem); | |
AppendLog(_T("XTYP_ADVSTOP conv=%lx topicName=%s itemName=%s"), | |
hconv, &topicName[0], &itemName[0]); | |
// 登録済みアイテムから除去する | |
std::map<HCONV, std::list<Item> >::iterator ite = itemsMap.find(hconv); | |
if (ite != itemsMap.end()) { | |
std::list<Item> &items = ite->second; | |
for (std::list<Item>::iterator ite = items.begin(); | |
ite != items.end(); ++ite) | |
{ | |
const Item& item = *ite; | |
if (item.hszItem == hszItem && item.hszTopic == hszTopic) { | |
items.erase(ite); | |
AppendLog(_T("Item Removed")); | |
break; | |
} | |
} | |
} | |
return (HDDEDATA) TRUE; | |
} | |
case XTYP_ADVREQ: | |
{ | |
AppendLog(_T("XTYP_ADVREQ")); | |
if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) { | |
AppendLog(_T("XTYP_ADVREQ conv=%lx reject uFmt=%d"), hconv, uFmt); | |
return (HDDEDATA)0; | |
} | |
// このメッセージはサーバーがDdePostAdvise関数を呼んだときに送られてくる。 | |
// このメッセージを受けたときにサーバーはクライアントにデーターを送る。 | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
std::vector<TCHAR> itemName = handleToString(hszItem); | |
// 応答文字列の作成 | |
std::vector<TCHAR> uniBuf = GetResponseText(&topicName[0], &itemName[0]); | |
AppendLog(_T("XTYP_ADVREQ conv=%lx topicName=%s itemName=%s flags=%ld result=%s"), | |
hconv, &topicName[0], &itemName[0], dwData1, &uniBuf[0]); | |
// バイトデータとして返却する | |
return stringToData(&uniBuf[0], hszItem, uFmt); | |
} | |
/* | |
* VBAのDDEExecuteなどから呼び出される. | |
* (引数はUnicodeでわたってくる) | |
*/ | |
case XTYP_EXECUTE: | |
{ | |
// コマンドの受信 | |
// hszTopicにトピック名、hdataにコマンドが格納されている。 | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
// ハンドルからデータのアドレスを得る | |
std::vector<BYTE> data = dataHandleToBytes(hdata); | |
DdeFreeDataHandle(hdata); | |
AppendLog(_T("XTYP_EXECUTE: conv=%lx topicName=%s uFmt=%ld command=%s"), | |
hconv, &topicName[0], uFmt, &data[0]); | |
//return DDE_FNOTPROCESSED; // 処理しない場合 | |
return (HDDEDATA)DDE_FACK; | |
} | |
case XTYP_POKE: | |
{ | |
if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) { | |
AppendLog(_T("XTYP_POKE conv=%lx reject uFmt=%d"), hconv, uFmt); | |
// ExcelのDDEPokeは値としてRange等のCellオブジェクトを指定しないと | |
// データが全く送信されない. | |
// Range等を指定した場合は、送信形式を繰り返し試行してくるため | |
// 受け入れ可能な形式に対してのみ応答すること. | |
return (HDDEDATA)0; | |
} | |
// send unsolicited data to the server | |
std::vector<TCHAR> topicName = handleToString(hszTopic); | |
std::vector<TCHAR> itemName = handleToString(hszItem); | |
// ハンドルからデータのアドレスを得る | |
std::vector<BYTE> data = dataHandleToBytes(hdata); | |
//std::vector<TCHAR> strCmd(reinterpret_cast<LPCSTR>(&data[0])); | |
DdeFreeDataHandle(hdata); | |
AppendLog(_T("POKE: conv=%lx topicName=%s itemName=%s data=%s"), | |
hconv, &topicName[0], &itemName[0], &data[0]); | |
return (HDDEDATA)DDE_FACK; | |
} | |
default: | |
return (HDDEDATA) FALSE; | |
} | |
} | |
/** | |
* 画面サイズとログの行数から、スクロールバーの大きさを算定、設定する | |
*/ | |
void CalcScrollBar(HWND hWnd) | |
{ | |
RECT rct; | |
GetClientRect(hWnd, &rct); | |
int height = rct.bottom; | |
PAINTSTRUCT ps = {0}; | |
HDC hdc = BeginPaint(hWnd, &ps); | |
TEXTMETRIC metric = {0}; | |
GetTextMetrics(hdc, &metric); | |
int numOfDisplayLines = height / metric.tmHeight; | |
int count = logs.size(); | |
SCROLLINFO scr = {0}; | |
scr.cbSize = sizeof(SCROLLINFO); | |
scr.fMask = SIF_PAGE | SIF_RANGE; | |
scr.nMin = 0; | |
scr.nMax = count - 1; | |
scr.nPage = numOfDisplayLines; | |
#ifdef _DEBUG | |
std::vector<TCHAR> buf(512); | |
wsprintf(&buf[0], _T("nPage=%d count=%d\n"), scr.nPage, scr.nMax); | |
OutputDebugString(&buf[0]); | |
#endif | |
SetScrollInfo(hWnd , SB_VERT , &scr , TRUE); | |
} | |
/** | |
* ウィンドウが作成されるときに呼び出される | |
*/ | |
void OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) | |
{ | |
// DDEサービスの初期化 | |
m_idInst = 0; | |
if (DdeInitialize(&m_idInst, DdeCallback, APPCLASS_STANDARD, 0L) != DMLERR_NO_ERROR) { | |
return; | |
} | |
// サービス名を文字列ハンドルにする (いらなくなったらハンドルを解放すること) | |
m_hszServiceName = DdeCreateStringHandle(m_idInst, SERVICE_NAME, CP_WINNEUTRAL); | |
// DDEサービスを登録する | |
HDDEDATA ret = DdeNameService(m_idInst, m_hszServiceName, 0, DNS_REGISTER); | |
// コールバックをすべて有効とする | |
DdeEnableCallback(m_idInst, 0, EC_ENABLEALL); | |
// スクロールバーの設定 | |
CalcScrollBar(hWnd); | |
} | |
/** | |
* DDEのアドバイズループで定期的にデータ変更を通知するためのタイマー | |
*/ | |
void OnTimer(HWND hWnd, WPARAM, LPARAM) | |
{ | |
for (std::map<HCONV, std::list<Item> >::iterator iteMap = itemsMap.begin(); | |
iteMap != itemsMap.end(); ++iteMap) { | |
std::list<Item> &items = iteMap->second; | |
for (std::list<Item>::iterator ite = items.begin(); | |
ite != items.end(); ++ite) | |
{ | |
const Item& item = *ite; | |
std::vector<TCHAR> topicName = handleToString(item.hszTopic); | |
std::vector<TCHAR> itemName = handleToString(item.hszItem); | |
AppendLog(_T("[OnTimer]--> DdePostAdvise topic=%s item=%s"), &topicName[0], &itemName[0]); | |
DdePostAdvise(m_idInst, item.hszTopic, item.hszItem); | |
} | |
} | |
} | |
/** | |
* ログを描画する | |
*/ | |
void OnPaint(HWND hWnd, WPARAM, LPARAM) | |
{ | |
PAINTSTRUCT ps = {0}; | |
HDC hdc = BeginPaint(hWnd, &ps); | |
TEXTMETRIC metric = {0}; | |
GetTextMetrics(hdc, &metric); | |
SCROLLINFO scr = {0}; | |
scr.cbSize = sizeof(SCROLLINFO); | |
scr.fMask = SIF_POS | SIF_TRACKPOS | SIF_RANGE | SIF_PAGE; | |
GetScrollInfo(hWnd, SB_VERT, &scr); | |
int pos = scr.nPos; | |
int page = scr.nPage; | |
int cnt = 0; | |
int idx = 0; | |
for (std::list<LogData>::const_iterator ite = logs.begin(); | |
ite != logs.end(); ++ite) { | |
if (idx >= pos) { | |
int y = cnt * metric.tmHeight; | |
const LogData& logData = *ite; | |
LPCTSTR message = &logData.message[0]; | |
int total = lstrlen(message) + 32; | |
std::vector<TCHAR> buf(total + 1); | |
wsprintf(&buf[0], _T("[%05d] %02d:%02d.%03d:%02d %s"), | |
logData.number, | |
logData.systime.wHour, | |
logData.systime.wMinute, | |
logData.systime.wSecond, | |
logData.systime.wMilliseconds, | |
message); | |
TextOut(hdc, 5, y, &buf[0], _tcslen(&buf[0])); | |
cnt++; | |
} | |
if (cnt > page) { | |
break; | |
} | |
idx++; | |
} | |
EndPaint(hWnd, &ps); | |
} | |
/** | |
* メインウィンドウを破棄する. | |
*/ | |
void OnDestroy(HWND hWnd) | |
{ | |
if (m_idInst) { | |
// DDEMLコールバックを停止 | |
DdeEnableCallback(m_idInst, 0, EC_DISABLE); | |
// DDEサービスを登録解除 | |
DdeNameService(m_idInst, 0, 0, DNS_UNREGISTER); | |
// 使用済み文字列ハンドルを解放する | |
DdeFreeStringHandle(m_idInst, m_hszServiceName); | |
// DDEMLを初期化解除する | |
DdeUninitialize(m_idInst); | |
} | |
PostQuitMessage(0); | |
} | |
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (message) | |
{ | |
case WM_CREATE: | |
OnCreate(hWnd, wParam, lParam); | |
break; | |
case WM_TIMER: | |
OnTimer(hWnd, wParam, lParam); | |
InvalidateRect(hWnd, NULL, TRUE); | |
break; | |
case WM_PAINT: | |
OnPaint(hWnd, wParam, lParam); | |
break; | |
case WM_VSCROLL: | |
{ | |
SCROLLINFO scr = {0}; | |
scr.cbSize = sizeof(SCROLLINFO); | |
scr.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; | |
GetScrollInfo(hWnd, SB_VERT, &scr); | |
switch(LOWORD(wParam)) { | |
case SB_TOP: | |
scr.nPos = scr.nMin; | |
break; | |
case SB_BOTTOM: | |
scr.nPos = scr.nMax; | |
break; | |
case SB_LINEUP: | |
if (scr.nPos) { | |
scr.nPos--; | |
} | |
break; | |
case SB_LINEDOWN: | |
if (scr.nPos < scr.nMax) { | |
scr.nPos++; | |
} | |
break; | |
case SB_PAGEUP: | |
scr.nPos -= scr.nPage; | |
break; | |
case SB_PAGEDOWN: | |
scr.nPos += scr.nPage; | |
break; | |
case SB_THUMBPOSITION: | |
case SB_THUMBTRACK: | |
scr.nPos = HIWORD(wParam); | |
break; | |
} | |
SetScrollInfo(hWnd, SB_VERT, &scr, TRUE); | |
} | |
InvalidateRect(hWnd, NULL, TRUE); | |
break; | |
case WM_SIZE: | |
CalcScrollBar(hWnd); | |
InvalidateRect(hWnd, NULL, TRUE); | |
break; | |
case WM_DESTROY: | |
OnDestroy(hWnd); | |
break; | |
default: | |
return DefWindowProc(hWnd, message, wParam, lParam); | |
break; | |
} | |
return 0; | |
} | |
int APIENTRY WinMain(HINSTANCE hInstance, | |
HINSTANCE hPrevInstance, | |
LPSTR lpCmdLine, | |
int nCmdShow ) | |
{ | |
WNDCLASSEX wcex; | |
wcex.cbSize = sizeof(WNDCLASSEX); | |
wcex.style = CS_HREDRAW | CS_VREDRAW; | |
wcex.lpfnWndProc = WndProc; | |
wcex.cbClsExtra = 0; | |
wcex.cbWndExtra = 0; | |
wcex.hInstance = hInstance; | |
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); | |
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); | |
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); | |
wcex.lpszMenuName = NULL; | |
wcex.lpszClassName = szWindowClass; | |
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); | |
if (!RegisterClassEx(&wcex)) { | |
MessageBox(NULL, | |
_T("Call to RegisterClassEx failed!"), | |
TITLE_NAME, | |
MB_ICONERROR | MB_OK); | |
return 1; | |
} | |
hInst = hInstance; | |
HWND hWnd = CreateWindow( | |
szWindowClass, | |
TITLE_NAME, | |
WS_OVERLAPPEDWINDOW | WS_VSCROLL, | |
CW_USEDEFAULT, CW_USEDEFAULT, | |
250, 100, | |
NULL, | |
NULL, | |
hInstance, | |
NULL | |
); | |
if (!hWnd) { | |
MessageBox(NULL, | |
_T("Call to CreateWindow failed!"), | |
TITLE_NAME, | |
MB_ICONERROR | MB_OK); | |
return 1; | |
} | |
AppendLog(TITLE_NAME); | |
SetTimer(hWnd, 1, 5000, NULL); | |
ShowWindow(hWnd, nCmdShow); | |
UpdateWindow(hWnd); | |
MSG msg; | |
while (GetMessage(&msg, NULL, 0, 0)) { | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
return (int) msg.wParam; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sub test_dde() | |
Dim ch | |
ch = DDEInitiate("DDESRV", "AAA") | |
DDEExecute ch, "ls -lsR" | |
DDEPoke ch, "BBB", ActiveSheet.Range("A1") | |
DDETerminate ch | |
End Sub |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment