进程间调用[合集]

Linux进程间的“秘密通道”:IPC核心原理与管道(Pipe)实战 Linux进程间通信(IPC)是打破内存隔离、实现进程协作的关键技术。文章分析了四种主流IPC方式:1)管道(Pipe)——适用于亲缘进程的简单单向通信;2)信号(Signal)——轻量级异步通知机制;3)共享内存/mmap——最高速的数据共享方案;4)本地套接字(Socket)——最通用的稳定通信接口。通过pipe()函数的代码实例,演示了父子进程间建立单向通信管道的具体实现流程。不同IPC方式各有特点:管道简单但受限,信号快速但功能单一,共享内存高效但需同步,套接字通用但稍复杂。理解这些核心机制是从单进 阅读详情

1。WriteProcessMemory

如何更改其它程序 ListView 控件中某个 Item 的内容

作者:天津/赵春生

下载源代码

一:程序说明

  这次我将介绍如何更改其他程序ListView控件中某个Item的内容,关于类似的拙文我已经写了两篇,这是第三篇,本篇和第一篇《如何向其他程序的 ListView 控件发送 LVM_GETITEMTEXT 消息》类似,区别在于:

发送的消息不同:前者是读取pszText的内容——发送LVM_GETITEMTEXT;这次是设置pszText,应该发送LVM_SETITEMTEXT;
字符串缓冲区的作用不同:前者pItem用来接收ITEMTEXT,我们可以通过ReadProcessMemory函数来读取其内容;而在本篇中p_MyItemText则用来存放我们要设置的ITEMTEXT,并用WriteProcessMemory函数将其写入到目标程序中。
作为演示,下面的这段程序将更改TaskManager中第6个项目中第1列的内容。程序运行后的效果图:

二:具体实践
//////////////////////////////////////////////////////////////////////////////////
/*
 * Send LVM_SETITEMTEXT
 * 本程序适用于:Win2KP+SP4[Windows TaskManager(5.0.2195.6620)]
 *     WinXP+SP1[Windows TaskManager]
 * 代码在Win2000P+SP4 + VC6+SP6测试通过
*/
//////////////////////////////////////////////////////////////////////////////////

#include<windows.h>
#include<commctrl.h>

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
     
{
 HWND hwnd;
 int iItem=0;
 LVITEM lvitem, *plvitem;
 DWORD PID;
 HANDLE hProcess;
 char *p_MyItemText;//目标程序中用来存放TEXT的地址

 //The pszText member is the pointer to a null-terminated
 //  string containing the new text; it can also be NULL.
 //以上信息是从API手册中获得的,故在本例中字符串长度不能>=12,
 //以确保字符串后有NULL。
 char str_MyItemText[12]={0};
 strcpy(str_MyItemText,"天津 赵春生");
 
 
 hwnd=FindWindow("#32770","Windows 任务管理器");
 hwnd=FindWindowEx(hwnd,0,"#32770",0);
 hwnd=FindWindowEx(hwnd,0,"SysListView32",0);

 if (!hwnd)
  MessageBox(NULL,"[Windows 任务管理器] 尚未启动!","错误!",NULL);
 else
 {
  GetWindowThreadProcessId(hwnd, &PID);
   
  hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,PID);
  if (!hProcess)
   MessageBox(NULL,"获取进程句柄操作失败!","错误!",NULL);
  else
  {
   plvitem=(LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
   p_MyItemText=(char*)VirtualAllocEx(hProcess, NULL, 12, MEM_COMMIT, PAGE_READWRITE);

   if ((!plvitem)||(!p_MyItemText))
    MessageBox(NULL,"无法分配内存!","错误!",NULL);
   else
   {
    MessageBox(NULL,"本演示程序将更改TaskManager中第6个项目中第1列的内容。","提示",NULL);
    
    iItem=5;//5在这里是第六个(从零开始)
    lvitem.iSubItem=0;//同上
    lvitem.pszText=p_MyItemText;
    
    WriteProcessMemory(hProcess, p_MyItemText, &str_MyItemText, 12, NULL);
    WriteProcessMemory(hProcess, plvitem, &lvitem, sizeof(LVITEM), NULL);
    //向目标程序发送LVM_SETITEMTEXT消息
    SendMessage(hwnd, LVM_SETITEMTEXT, (WPARAM)iItem, (LPARAM)plvitem);
   }
  }
 }
 
 //释放内存
 CloseHandle(hwnd);
 CloseHandle(hProcess);
 VirtualFreeEx(hProcess, plvitem, 0, MEM_RELEASE);
 VirtualFreeEx(hProcess, p_MyItemText, 0, MEM_RELEASE);
 
 return 0;
}

住: VirtualAllocEx在WINCE下不支持,因为CE不支持在其他进程的进程空间中分配内存,所以上面这个例子在CE下是行不通的,不过可以采用其他比如共享内存等

2。WM_COPYDATA

转一篇文章,PPC下的:

Introduction
Most PocketPC programming is done in MFC using wizards. If instead you like to program in plain Win32/C++, you'll have found there to be not enough documentation or examples. This article and the accompanying source code provide a complete minimal example - a Notepad clone. It covers:

The DocList control (i.e. front page list of files, like in Pocket Word)
The "Card" architecture (that's what PocketPC calls an opened document window)
The Soft Input Panel (SIP, the keyboard/transcriber)
I haven't tried to wrap up the Windows functionality into my own API. That's because I think people want to learn the Windows API itself, not my own API. And also, the Windows API for PocketPC is rather clean and simple already. PocketPC is actually the only native implementation of Win32: it doesn't rest upon an internal layer like NT/Win2K/XP, and it doesn't rest upon 16bit like Win95/98/ME.

Program structure
PocketPC C++ programs are written using Microsoft eMbedded Visual Tools 3.0 (free). You should also download a free STL port for eVC++, by Giuseppe Govi. Although this article doesn't use it.

To write clean programs for PocketPC, you must (as Yoda says) unlearn what you have learnt:

Keep a small memory footprint.
There will never be more than one instance of your app running at a time; nor will there be more than one document open at a time.
Therefore, global variables are good. Per-app variables can be global. Per-document variables can be global.
Because the variables are declared statically, not created dynamically, it means that memory-allocation need not be done at runtime. This is a good thing.
Incidentally, the development environment often fails to build unless the "WCE Configuration" toolbar is visible. (This is the toolbar with drop-downs to select target OS, target device, target processor).

Preliminaries
These are the header files and app-specific global variables. Also, under Project > Settings > Linker > Input, you must link against doclist.lib, aygshell.lib and note_prj.lib.

#include <windows.h>
#include <aygshell.h>   // for SIP
#include <doclist.h>    // for DocList control
#include <projects.h>   // for Folder drop-down
#include <newmenu.h>    // for New menu
#include <commctrl.h>
#include <shellapi.h>
#ifndef IDM_NEWMENUMAX        // newmenu.h was already
#include <newmenu.h>    // included in PPC2000
#endif

HINSTANCE hInstance;
HWND hmain=0;       // Main window
HWND hmbar=0;       // Menu-bar
HWND hdlc=0;        // DocList control
HWND hcard=0;       // Document window
HWND hed=0;         // Edit window inside Doc-window
wchar_t dlcfolder[MAX_PATH]={0}; // current DLC folder
SHACTIVATEINFO shai={sizeof(SHACTIVATEINFO),0,0,0,0,0};
bool close_on_ok=false;

The window layout: there is a main window hmain. It owns a menu-bar hmbar which sits underneath. In the client area is a DocList control hdlc and a document-window hcard occupying the same area; only one will be visible at a time. Our app is a Notepad-clone, so we have an edit-window hed inside.

The variable dlcfolder holds the currently-selected folder by the DocList. That's because, when it comes to saving a new document, this will tell us which folder to put it in.

The SHACTIVATEINFO shai is used by the system to maintain the SIP's state: there are standard calls we must make in WM_ACTIVATE and WM_SETTINGCHANGED, involving this structure, for the SIP to behave properly.

And close_on_ok is for the following reason. If the user clicked on an associated document within File Explorer and so opened our application, then the OK button should close us and return to File Explorer. But if the user opened us from the Start menu, then closing the document should return the user to the main DocList control. This flag records which mode of closure was most recently indicated.

// At the start of WinMain:

hmain = FindWindow(L"LuMainClass",L"My App");
if (hmain!=0)
{
  SetForegroundWindow((HWND)((ULONG)hmain|1));
  // the |1 will activate any owned windows
  if (arg!=0 && wcslen(arg)!=0)
  {
    COPYDATASTRUCT cs;
    cs.dwData=0;
    cs.cbData=2+2*wcslen(arg);
    cs.lpData=arg;
    SendMessage(hmain,WM_COPYDATA,NULL,(LPARAM)&cs);
  }
  else SendMessage(hmain,WM_APP,0,0);
  return 0;
}
To save on memory, only one instance of our app should ever be loaded. When we're loaded, we check if an instance already exists. If we're loaded with a filename argument to open, we have to communicate this filename to the other instance: hence WM_COPYDATA. We use WM_APP to signal a general wakeup, whereupon some refreshes are performed.

// within WinMain, after registering classes:

SHInitExtraControls();

hmain = CreateWindow(L"LuMainClass", L"My App", WS_VISIBLE,
    CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
    NULL, NULL, hInstance, NULL);
ShowWindow(hmain,nCmdShow);
UpdateWindow(hmain);
if (arg!=0 && wcslen(arg)!=0)
{ COPYDATASTRUCT cs; cs.dwData=0;
  cs.cbData=2+2*wcslen(arg); cs.lpData=arg;
  SendMessage(hmain,WM_COPYDATA,NULL,(LPARAM)&cs);
}

Using SHInitExtraControls() allows us to use CAPEDIT (like a normal EDIT but it capitalizes the first letter) and also WC_SIPPREF (automatic management of the SIP).

Managing the windows
We start with creation and destruction of the application window. Note that most of our child windows need not be destroyed, since that happens automatically. The exception is the menu-bar, which must be destroyed explicitly.

case WM_CREATE:
{ SHMENUBARINFO mbi; ZeroMemory(&mbi,sizeof(mbi));
  mbi.cbSize=sizeof(mbi);
  mbi.hwndParent = hwnd;
  mbi.nToolBarId = 101;
  mbi.hInstRes = hInstance;
  SHCreateMenuBar(&mbi);
  hmbar = mbi.hwndMB;

  SHGetSpecialFolderPath(hwnd,dlcfolder,CSIDL_PERSONAL,TRUE);
  DOCLISTCREATE dlc; ZeroMemory(&dlc,sizeof(dlc));
  dlc.dwStructSize=sizeof(dlc);
  dlc.hwndParent=hwnd;
  dlc.pstrFilter = L"Text/0*.txt/0";
  dlc.wId=102;
  hdlc = DocList_Create(&dlc);

  hcard = CreateWindow(L"LuCardClass",L"",WS_CHILD,0,0,0,0,
           hwnd,(HMENU)103,hInstance,0);
  hed = CreateWindow(L"CAPEDIT",L"",WS_CHILD|WS_VISIBLE
           |WS_HSCROLL|WS_VSCROLL|ES_AUTOHSCROLL
           |ES_AUTOVSCROLL|ES_MULTILINE|WS_TABSTOP,0,0,0,0,
           hcard,(HMENU)104,hInstance,0);
  CreateWindow(WC_SIPPREF,L"",WS_CHILD,0,0,0,0,
           hcard,(HMENU)105,hInstance,0);

  MakeLayout(mlShowHide|mlRefreshDocList|mlResize,0);

  int nitems=DocList_GetItemCount(hdlc);
  if (nitems==0)
  { SendMessage(hwnd,WM_COMMAND,IDM_NEWMENUMAX+1,0);
  }
} return 0;

case WM_HIBERNATE:
{ if (!doc.open) SetWindowText(hed,L"");
} return 0;

case WM_DESTROY:
{ if (doc.open && doc.changed) doc.Save();
  CommandBar_Destroy(hmbar);
  PostQuitMessage(0);
} return 0;
The WM_DESTROY message is an odd case. It is never called during the normal operation of a program. That's because, if the user clicks the Close button, it doesn't close but merely minimizes the app (so that, subsequently, the app can be opened up instantly). If the system is running low on memory, it sends a WM_HIBERNATE message asking applications to clean themselves up. If it's running really low, it sends a WM_CLOSE which (in DefWindowProc) calls DestroyWindow, so finally triggering our WM_DESTROY.

The resources for the menubar are as follows. I'm writing it here because it doesn't seem to be documented elsewhere.

// A resource file for a menubar
#include <windows.h>
#include <commctrl.h>
#include <aygshell.h>

101 RCDATA MOVEABLE
BEGIN
  101, 3,
  I_IMAGENONE, IDM_SHAREDNEW, TBSTATE_ENABLED,
       TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_SHNEW, 0, 0,
  I_IMAGENONE, 202, TBSTATE_ENABLED,
       TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, 202, 0, 1,
  I_IMAGENONE, 203, TBSTATE_ENABLED,
       TBSTYLE_AUTOSIZE, 203, 0, NOMENU,
END
This RCDATA is for a menubar. The first row (101,3) says to look up MENU#101, and that there will be three items in this menubar. In each row, the second item (IDM_SHAREDNEW, 203, 203) is the WM_COMMAND id that will be sent upon clickage. Here, "Shared New" means that it's a menu that the system will fill up with all the standard items (Task, Note, ...) and then will offer to us to fill up the rest.

STRINGTABLE DISCARDABLE
BEGIN
   201 "DummyNew"
   202 "Tools"
   203 "Edit"
END
Within each row of RCDATA, the third-last item (IDS_SHNEW, 202, 203) is an index into the STRINGTABLE for the screen-names of each item. IDS_SHNEW is a system-defined string.

101 MENU DISCARDABLE
BEGIN
  POPUP ""
  BEGIN
      MENUITEM "DummNewEntry" -1
  END
  POPUP ""
  BEGIN
      MENUITEM "Exit" 301
  END
END
The final item in each row of RCDATA says which menu popup to choose from the MENU#101 resource. The first item (0) is actually ignored, since it is overridden by IDM_SHAREDNEW. The second item (1) points to popup index 1. And the third item (NOMENU) says that there is no popup here.

The interaction between IDM_SHAREDNEW and the popup number is a bit idiosyncratic:

IDM_SHAREDNEW, popup#0 -- uses the global menu, but without an arrow
IDM_SHAREDNEW, NOMENU -- global menu, with uparrow, and 'new' acts like a button not a menu.
cmd#201, popup#0 -- just our own menu, exactly like the others
cmd#201, NOMENU -- has the uparrow, and a button, but neither does anything
MakeLayout
The function MakeLayout is as follows. It's job is to rearrange and refresh windows as necessary according to the current state.

// MakeLayout - this shows/hides the edit/doclist as approp-
// riate, resizes according to SIP, refreshes the DocList.
//
const DWORD mlShowHide=0, mlRefreshDocList=1, mlResize=2;

void MakeLayout(DWORD flags, LPARAM wmsc_lparam=0)
{ ShowWindow(hcard, doc.open?SW_SHOW:SW_HIDE);
  ShowWindow(hdlc, doc.open?SW_HIDE:SW_SHOW);
  // the 'edit' button
  TBBUTTONINFO tbbi; ZeroMemory(&tbbi,sizeof(tbbi));
  tbbi.cbSize=sizeof(tbbi); tbbi.dwMask=TBIF_STATE;
  tbbi.fsState= (doc.open&&doc.readonly)?TBSTATE_ENABLED
                                        :TBSTATE_HIDDEN;
  SendMessage(hmbar,TB_SETBUTTONINFO,203,(LPARAM)&tbbi);
  // readonly in the card itself
  SendMessage(hed,EM_SETREADONLY,doc.readonly?TRUE:FALSE,0);
  if (doc.open && !doc.readonly) SetFocus(hed);
Like most PocketPC apps, by default we open documents in 'readonly' mode (indicated by our edit control having ES_READONLY). The user can click the "Edit" button in the menubar to switch to editing mode.

  // OK/X. If OK is hidden, then X will be visible
  SHDoneButton(hmain, doc.open?SHDB_SHOW:SHDB_HIDE);
Windows can have either an X, or an OK, or nothing at all in their top right. Notionally the OK is "stronger" than the X: thus, if both are enabled, only the OK will be visible. It's normally to show OK when you're editing a document, and X when you're in DocList mode.

  // Now resize the windows to accommodate SIP. If this
  // is called from WM_SETTINGSCHANGE, then use the same
  // lParam for efficency; otherwise set wmsc_lparam=0
  if (flags&mlResize)
  { SIPINFO si;
    SHSipInfo(SPI_GETSIPINFO,wmsc_lparam,&si,0);
    int x=si.rcVisibleDesktop.left;
    int y=si.rcVisibleDesktop.top;
    int w=si.rcVisibleDesktop.right-x;
    int h=si.rcVisibleDesktop.bottom-y;
    RECT rc; GetWindowRect(hmbar,&rc);
    int mh=rc.bottom-rc.top;
    if ((si.fdwFlags&SIPF_ON)) h+=1; else h-=mh-1;
    MoveWindow(hmain,x,y,w,h,FALSE);
    GetClientRect(hmain,&rc);
    MoveWindow(hcard,0,0,rc.right,rc.bottom,FALSE);
    MoveWindow(hdlc,0,0,rc.right,rc.bottom,TRUE);
    MoveWindow(hed,0,0,rc.right,rc.bottom,FALSE);
  }
  // We need to refresh after files have been changed,
  // to reshow their time/size
  if (!doc.open && (flags&mlRefreshDocList))
  { DocList_Refresh(hdlc);
  }
}
It is costly to get the SIP info and also to refresh the DocList. That's why we control both by flags, to be done only if necessary.

Activation and commands
The responses to WM_ACTIVATE and WM_SETTINGCHANGE are standard, required by the system to handle the SIP. Also, we call MakeLayout, to accommodate the new size of the SIP:

case WM_ACTIVATE:
{ SHHandleWMActivate(hwnd, wParam, lParam, &shai, FALSE);
} return 0;

case WM_SETTINGCHANGE:
{ SHHandleWMSettingChange(hwnd, wParam, lParam, &shai);
  MakeLayout(mlResize,lParam);
} return 0;
Also, when our app was running in the background but another instance was launched, then we have to wake up the old instance: either WM_APP if we just need to activate it, or WM_COPYDATA if we need to get it to open a document.

case WM_APP:
{ // we are a background instance being reactivated
  close_on_ok=false;
  // maybe have to refresh the doclist.
  if (!doc.open) MakeLayout(mlRefreshDocList);
} return 0;

case WM_COPYDATA:
{ // we are a background instance being told to open a doc.
  if (doc.open && doc.changed) doc.Save();
  close_on_ok=true;
  COPYDATASTRUCT *cs = (COPYDATASTRUCT*)lParam;
  doc.Open((wchar_t*)cs->lpData);
  MakeLayout(mlShowHide);
} return 0;
And this is the code to respond to clicks on the menubar and on the OK button:

case WM_COMMAND:
{ int id=LOWORD(wParam);
  if (id==IDOK && doc.open) // OK button
  { if (doc.open && doc.changed) doc.Save();
    SHSipPreference(hmain,SIP_FORCEDOWN);
    doc.open=false; MakeLayout(mlShowHide|mlRefreshDocList);
    if (close_on_ok) ShowWindow(hwnd,SW_MINIMIZE);
  }
The OK button means that the user has finished editing the current document. That's why we save it. We also force down the SIP. That's because we know the next thing that the user sees will be the DocList control, and we know that this doesn't need the SIP: without the force-down, the SIP would wait two seconds before hiding.

Note also the close_on_ok. If the user had opened up most recently from an outside source (e.g. within File Explorer, clicked on an app associated with our app), then a subsequent click on OK should return straight to File Explorer.

In the following, we respond to a menu item Tools > Exit, by terminating. I include this for debugging purposes. The menu item should be removed in the release version of the code.

  // ... continued from WM_COMMAND

  else if (id==301) // tools.exit
  { DestroyWindow(hwnd);
  }
  else if (id==203) // tools.edit
  { if (doc.open && doc.readonly)
    { doc.readonly=false; MakeLayout(mlShowHide);
    }
  }
  else if (id==IDM_NEWMENUMAX+1) // new.text
  { if (doc.open && doc.changed) doc.Save();
    doc.New(); MakeLayout(mlShowHide);
  }
} return 0;


case WM_NOTIFY:
{ NMHDR *nmhdr = (NMHDR*)lParam;
  if (nmhdr->code==NMN_GETAPPREGKEY)
  { // Called by the system for us to fill in our New menu.
    NMNEWMENU *nmnew = (NMNEWMENU*)lParam;
    AppendMenu(nmnew->hMenu, MF_ENABLED,
               IDM_NEWMENUMAX+1, L"Text");
    AppendMenu(nmnew->hMenu, MF_SEPARATOR, 0, 0);
  }
  else if (nmhdr->code==DLN_ITEMACTIVATED)
  { DLNHDR *dln = (DLNHDR*)lParam;
    doc.Open(dln->pszPath);
    MakeLayout(mlShowHide);
  }
  else if (nmhdr->code==DLN_FOLDER)
  { DLNHDR *dln = (DLNHDR*)lParam;
    GetPathForFolder(dln->pszPath,dlcfolder);
  }
} return 0;
The response to DLN_FOLDER means that the user has clicked on a folder from the DocList's drop-down folder list. We call our own function (below) to retrieve this folder's full path. That's so that, when we subsequently save a new document, we can save it into the place the user was looking at. We need a separate function because dln->pszPath merely gives the folder name, not its full path.

The are bugs in the DocList control. It's meant to display folders from My Documents and every storage card, and it does, but it only allows the user to select folders from the first storage card or My Documents - secondary storage cards don't work. This is problematic on some devices which have a permanent built-in primary storage card, and external SDs count as secondary! Another problem is that, if a folder "Name" exists in two different locations, it collapses them into just one entry -- unless they happen to have different capitalizations, in which case it collapses them for some purposes (selection) but not for other purposes (display).

// GetPathForFolder(const wchar_t *folder, wchar_t *path):
// Given a folder name we return its path
//   All Folders  -->  /My Documents
//   Business     -->  /My Documents/Business
//   MySdFolder   -->  /Storage Card/MySdFolder
// Assume path>=MAX_PATH.

struct EnumProjectsInfo
{ const wchar_t *folder; wchar_t *path;
};

BOOL CALLBACK EnumCallback(PAstruct *pa, LPARAM lp)
{ wchar_t *fn;
  if (pa->m_IDtype!=FILE_ID_TYPE_OID) fn=pa->m_szPathname;
  else
  { CEOIDINFO cinf;
    CeOidGetInfo(pa->m_fileOID,&cinf);
    fn = cinf.infDirectory.szDirName;
  }
  const wchar_t *c=fn, *lastslash=c;
  while (*c!=0) {if (*c=='//') lastslash=c+1; c++;}
  EnumProjectsInfo *epi = (EnumProjectsInfo*)lp;
  if (wcsicmp(epi->folder,lastslash)!=0) return TRUE;
  // TRUE means keep enumerating
  wcscpy(epi->path,fn); return FALSE;
}

void GetPathForFolder(const wchar_t *folder, wchar_t *path)
{ // assume 'path' is at least MAX_PATH.
  *path=0;
  EnumProjectsInfo epi; epi.folder=folder; epi.path=path;
  EnumProjectsEx(EnumCallback,0,PRJ_ENUM_ALL_DEVICES,
                      (LPARAM)&epi);
  if (*path==0) SHGetSpecialFolderPath(NULL,epi.path,
                      CSIDL_PERSONAL,FALSE);
  }
}
The document
Our application is a version of Notepad. So, our document window is basically an EDIT control. (Actually, a CAPEDIT, which is unique to PocketPC and which capitalizes the first character). Here's how we represent our document:

struct TDocument
{ wchar_t fn[MAX_PATH];
  bool open, changed, readonly;
  bool unicode;

  TDocument() : open(false) {*fn=0;}
  void New();
  bool Open(const wchar_t *ofn);
  void Save();
} doc;
Note at the bottom: we declare a global static variable doc, for our document. Global variables make PocketPC applications much easier and cleaner. Most of the fields are obvious:

open indicates whether there is an open document (and hence whether the rest of the flags are meaningful)
readonly is because existing documents are normally opened in read-only mode; the user has to click Edit to start altering them
changed says whether any changes have been made (and hence have to be saved)
fn is the name of the file, if we opened an existing one, empty otherwise.
unicode says whether this file should be saved to disk in Unicode format. Unicode is native for PocketPC. But if we had opened an ASCII document, it would be rude to convert it.
LRESULT CALLBACK CardWndProc(HWND hwnd, UINT msg,
                             WPARAM wParam, LPARAM lParam)
{ if (msg==WM_COMMAND)
  { int id=LOWORD(wParam), code=HIWORD(wParam);
    if (id==104 && code==EN_CHANGE) doc.changed=true;
  }
  return DefWindowProc(hwnd,msg,wParam,lParam);
}
Most of the fields are set by the New/Open/Save methods, but changed is also set when there have been any changes to the edit control, above.

void TDocument::New()
{ *fn=0; SetWindowText(hed,L"");
  open=true; changed=false; readonly=false; unicode=true;
}

bool TDocument::Open(const wchar_t *ofn)
{ HANDLE hf=CreateFile(ofn,GENERIC_READ,0,
             NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  if (hf==INVALID_HANDLE_VALUE) return false;
  DWORD size=GetFileSize(hf,0); char *buf=new char[size+2];
  DWORD red; ReadFile(hf,buf,size,&red,NULL);
  buf[size]=0; buf[size+1]=0;
  CloseHandle(hf);
The interesting functionality in Open is to detect whether the file is Unicode or not. If it starts with the Unicode Byte-Order-Mark (BOM), we know it's Unicode. Otherwise, we'll guess, based on whether the first 256 bytes look ASCII-ish or not. That's because many systems save Unicode files without the BOM.

  // Unicode detector:
  wchar_t *unc=0;
  if (buf[0]==-1 && buf[1]==-2) unc=(wchar_t*)(buf+2);
  else
  { for (unsigned int i=3; i<256 && i<size && unc==0; i+=2)
    { if (buf[i]==0) unc=(wchar_t*)buf;
    }
  }
  unicode = (unc!=0);   
  //
  if (unicode) SetWindowText(hed,unc);
  else
  { unc = new wchar_t[size];
    MultiByteToWideChar(CP_ACP,0,buf,-1,unc,size);
    SetWindowText(hed,unc);
    delete[] unc;
  }
  wcscpy(fn,ofn);
  open=true; changed=false; readonly=true;
  return true;
}
As for Save(), its complication is that it might have to auto-generate a filename for a newly created document. That's the way of things in the PocketPC: trouble the user as little as possible with pesky things like FileOpen or FileSave. We make use of a trivial function isalphanum(), which is defined below:

void TDocument::Save()
{ if (!open || !changed) return;
  int len=GetWindowTextLength(hed); if (len==0) return;
  wchar_t *unc=new wchar_t[len+1];
  GetWindowText(hed,unc,len+1);

  // To construct the filename from the doc's content...
  if (*fn==0)
  { wcscpy(fn,dlcfolder); wchar_t *d=fn+wcslen(fn);
    *d='//'; d++; bool any=false;
    int i=0; wchar_t *c=unc;
    for (; !isalphanum(*c) && *c!=0 && i<32; i++) {}
    for (; isalphanum(c[i]) && c[i]!=0 && i<32; i++)
    { *d=c[i]; d++; any=true;
    }
    if (!any) {wcscpy(d,L"unnamed"); d+=wcslen(d);}
    wcscpy(d,L".txt");
    for (i=1; GetFileAttributes(fn)!=0xFFFFFFFF; i++)
    { wsprintf(d,L" (%i).txt",i);
    }
  }
There's a bug in PocketPC's handling of SD cards. Every now and again, like one time in 400, it simply fails to save a file onto the card -- even though all the functions report success. I don't know what to do about it. One idea is to use FILE_FLAG_WRITE_THROUGH to avoid lazy write caching. Another is to re-open the file after saving it (maybe even 30 seconds after saving it) to check whether it worked.

  HANDLE hf = CreateFile(fn,GENERIC_WRITE,0,NULL,
                  CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  if (unicode)
  { const char byteorder[2]={-1,-2};
    DWORD writ; WriteFile(hf,byteorder,2,&writ,NULL);
    WriteFile(hf,unc,sizeof(wchar_t)*len,&writ,NULL);
  }
  else
  { int size = WideCharToMultiByte(CP_ACP,WC_DEFAULTCHAR,
                                   unc,-1,0,0,NULL,NULL);
    char *buf = new char[size];
    WideCharToMultiByte(CP_ACP,WC_DEFAULTCHAR,
                        unc,-1,buf,size,NULL,NULL);
    DWORD writ; WriteFile(hf,buf,size,&writ,NULL);
    delete[] buf;
  }
  CloseHandle(hf);
  delete[] unc;
  changed=false;
}

bool isalphanum(const wchar_t c)
{ if (c>='a' && c<='z') return true;
  if (c>='A' && c<='Z') return true;
  if (c>='0' && c<='9') return true;
  if (c==' ' || c=='-') return true;
  return false;
}
The function isalphanum() assists in auto-constructing save filenames. Our idea: considering up to the first 32 characters, use the first span of alphanumeric characters as the filename.

Postscript
If anyone posts a complaint that the code doesn't compile, with a stdafx.h error, they had better post anonymously -- otherwise I will track them down and bite their heads off. To avoid decapitation, read your compiler documentation on Project Settings > Compiler > Precompiled Headers.

History
10 June 2003 - Now, also compiles under PPC2000 (as per Mike Dimmick's suggestion) and under PPC2003 (with eVC4).
About ljw1004

 Lucian is a researcher at Microsoft in Redmond. As a break from computer science theory, he likes to write practical programs every now and then. Especially screen savers!
Click here to view ljw1004's online profile.
 

用最“原始”的方式——普通文件,实现Linux进程间通信 但今天,我们要返璞归真,探讨一位最“原始”、最不起眼的参与者——既然是普通文件,那么它天然就具备了被任意进程访问的能力,从而打破了匿名管道的“血缘”限制。两个完全无关的进程,可以通过约定好一个文件路径,来实现通信。的进程间通信,或者在一些简单的、对性能要求不高的脚本场景中,文件IPC或许还能派上用场。它的存在,更多的是作为一个教学工具,帮助我们深刻理解。现在,我们来编写一个程序,让子进程在文件中写下一句话,然后父进程去读取它。这个“共享文件偏移量”的特性,既是文件IPC的关键,也是它最大的“坑”。 阅读详情

相关推荐

进程间相互调用

C#进程间相互调用,了解进程间调用方法。

进程相关函数调用

进程相关函数调用

weixin_73062623的博客 866

USB通讯速度翻倍秘诀:手把手教你用WinUsb替代HID协议(附Zadig驱动配置图解)

本文详细介绍了如何通过WinUsb技术替代传统HID协议,实现USB通讯速度的显著提升。从技术对比、实现原理到Zadig驱动配置图解,手把手教你完成免驱高速通讯方案,适用于数据采集、固件升级等场景,实测速度可达HID协议的2倍以上。

weixin_29168675的博客 133

多进程编程——常用调用函数

1.fork系统调用 fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。 #include <sys/types.h> #include <unistd.h> pid_t f

fancy的博客 1877

【ANSYS Workbench仿真】命令流:压力渗透pressure penetration

对与流体接触的part添加边界压力,使流体压力渗透点的接触应力与流体压力相等,以便得到真实的变形结果,就需要用到“pressure penetration”。 但是在ansys中,只能通过ADPL的形式添加,即在workbench中添加命令流的形式。此文主要介绍了**压力渗透的添加要点**和**命令流使用说明**。

路边小M's Blog 1万+

若依框架整合Flowable:从零构建企业级流程中心

本文详细介绍了如何将若依框架与Flowable工作流引擎整合,构建企业级流程中心。通过环境配置、依赖添加、数据库设置等核心步骤,实现流程定义管理、任务处理服务等关键功能,并结合实际案例展示请假审批流程的实现与动态表单集成。文章还提供了性能优化和生产建议,帮助开发者高效构建稳定可靠的工作流系统。

weixin_28365523的博客 228

嵌入式Linux-进程中常用的函数调用

execl()和execv()要求提供新程序的路径名,而 execlp()和 execvp()则允许只提供新程序文件名,系统会在由环境变量 PATH 所指定的目录列表中寻找相应的可执行文件,如果执行的新程序是一个 Linux 命令,这将很有用;系统调用 execve()可以将新程序加载到某一进程的内存空间,通过调用 execve()函数将一个外部的可执行文件加载到进程的内存空间运行,使用新的程序替换旧的程序,而进程的栈、数据、以及堆数据会被新程序的相应部件所替换,然后从新程序的 main()函数开始执行。

weixin_52694360的博客 1114

Creo模型装配总结

creo7.0的零件装配操作方法,建立基准面、新建约束、约束顺序

heda3的博客 1万+

进程间调用常用函数

1、异步 函数 WIN32 API WinExec(“one.exe”, SW_SHOWNORMAL);第二个参数只是WinExec函数的参数,不是one.exe的参数。 注意:第一个参数一定是ASCII 2、同步函数 system函数原型: int system(const char *command) 是C库函数 注意: 1)第一个参数为ASCII ,运行时会弹出黑框 2) system()只是做一个调用其他程序的工作,只要调用成功就返回0,不成功就返回-1。 3)如果调用进程的目录中含有空

taozhua的博客 1464

【AI智能体】AI 编程 + 飞书CLI + Skills 重塑开发新范式实战操作详解

AI 编程 + 飞书CLI + Skills 重塑开发新范式实战操作详解

congge 3666

初识 QML

QML 是一种声明性语言,允许根据用户界面的可视组件以及它们如何相互交互和相互关联来描述用户界面。QML 是一种高度可读的语言,旨在使组件能够以动态方式互连,并且允许组件在用户界面中轻松重复使用和自定义。 使用 Qt Quick,开发人员可以轻松地在 QML 中构建流体动画用户界面,并且可以选择将这些用户界面连接到任何后端 C++ 库。

青春不老,奋斗不止! 1万+

编程 - 回调函数、共享内存概念与使用,以及进程间函数调用的讨论

关于进程间函数调用,以及回调函数、共享内存的使用 最近在做项目时,需要调用一个第三方的库和框架,在项目进行一段时间之后,突然发现对该第三方的库以及框架的实现机制不太了解,现根据其函数声明以及使用方式推导其实现机制。如有出入,还请见谅。 1、问题描述 2、回调函数 3、共享内存 4、其实现的可能方式 5、其他可能的实现方式

bailang_zhizun的博客 4615

Apollo EM Planner中路径-速度解耦优化的数学建模与仿真分析

摘要 Apollo EM Planner采用路径-速度解耦优化策略解决自动驾驶运动规划问题。该算法将时空轨迹规划分解为路径规划和速度规划两个子问题:首先在静态环境下生成几何最优路径,再优化沿路径的速度曲线。通过五次多项式表示路径和速度,构建包含平滑性、安全性、舒适性的多目标优化函数,并引入弹性模型处理动态障碍物约束。算法具有解耦优化、弹性适应、实时求解等特点,适用于高速公路、城市交叉口等多种场景。数学建模包括路径几何优化、速度动态优化和弹性势能函数,最终生成满足车辆动力学和环境约束的安全轨迹。

走向CTO的路上... 933

Linux进程间通信-超级合集

Linux进程间通信(IPC)机制主要包括四类:传统Unix IPC(管道、信号)、SystemV IPC(共享内存、消息队列、信号量)、POSIX IPC(改进版SystemV IPC)和套接字。管道分为匿名管道(父子进程间单向通信)和命名管道(任意进程间通信),前者轻量但局限,后者通过文件系统实现跨进程通信。SystemV IPC提供高性能内核对象,共享内存无数据拷贝最快但需同步,消息队列支持结构化数据传输,信号量用于进程同步。POSIX IPC接口更简洁,支持跨平台。套接字支持本地和网络通信,灵活但性

我的博客主要是用来给我自己复习的,主要是上传一些知识点,小型的项目和功能模块,如果有什么问题或错误麻烦联系我! 775

基于深度学习的鸟类检测识别系统:YOLOv5 + YOLOv8 + YOLOv10 + UI界面 + 数据集

YOLOv5是当前目标检测领域非常流行的模型,具有良好的检测精度和实时推理速度。其网络结构相比前几代YOLO模型进行了优化,尤其是在检测精度和计算效率上有了显著提高。YOLOv5特点高效性:能够在较低的硬件环境下进行实时检测。易于部署和使用:YOLOv5提供了简单的API接口,方便开发者快速集成和使用。优异的精度:在多个数据集上测试,表现出色。本文详细介绍了基于YOLOv5、YOLOv8和YOLOv10的鸟类检测识别系统的设计与实现。

YOLO 1114

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

操作系统课设之Linux 进程间通信

操作系统课程设计 Linux进程通信 Linux消息通信机制

RongLin02的博客 5740

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

python进程间通信

if==“”:进程(21203)正在读取…进程(21201)正在写入…进程(21202)正在写入…ur1被写入到队列中从队列中提取到:ur1ur5被写入到队列中从队列中提取到:ur5ur2被写入到队列中从队列中提取到:ur2ur6被写入到队列中从队列中提取到:ur6ur7被写入到队列中从队列中提取到:ur7ur3被写入到队列中从队列中提取到:ur3ur4被写入到队列中从队列中提取到:ur4ur8被写入到队列中从队列中提取到:ur8。

2401_84563080的博客 236

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

【面试题】操作系统常见面试题合集

????备战实习,会定期的总结常考的面试题,大家一起加油! ???? ???? ???? ????往期文章: ????【面试题】计算机网络篇-10道常见面试题p1 ????【面试题】JVM篇-10道常见面试题p1 ????【面试题】Java并发篇-10道常见面试题p1 ????【面试题】Java基础篇-常见面试题总结p1 ????【面试题】Java基础篇-常见面试题总结p2 ????【面试题】MySQL常见面试题合集 ????【面试题】Java基础篇-常见面试题总结p3 ????Java集合面试题看这篇

小皮皮LL的博客 2425

【操作系统最全面试题合集】

什么是操作系统? 1.操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的程序,是计算机系统的内核与基石; 2.操作系统本质上是运行在计算机上的软件程序 ; 3.操作系统为用户提供一个与系统交互的操作界面 ; 4.操作系统分内核与外壳(我们可以把外壳理解成围绕着内核的应用程序,而内核就是能操作硬件的程序) 什么是系统调用呢? 这些系统调用按功能大致可分为如下几类: 1.设备管理。完成设备的请求或释放,以及设备启动等功能。 2.文件管理。完成文件的读、写、创建及删除等功能。 3

weixin_43372187的博客 776

Android面试合集

Android开发面试知识汇总

shiguang528的博客 1105

进程间关系下

进程间的关系 下 需要用一种方法知道哪一个进程组是前台进程,这样伪终端程序就知道将终端输入和终端产生的信号发送到何处。 #include <unistd.h> pid_t tcgetpgrp(int fd); int tcsetpgrp(int fd, pid_t pgrpid); 函数tcgetpgrp返回前台进程组的id,他和fd上打开终端相关联。 给出控制tty的文件描述符,通过tcgetsid函数,应用程序能获得首进程的进程组id。 #include <termio

qq_32783703的博客 252

【操作系统+Linux】吐血整理-操作系统+Linux合集

文章目录操作系统1、请你说一下进程和线程的区别?2、什么是临界资源?什么是临界区?3、谈一谈操作系统的地址转化4、请你说一下OS缺页置换算法5、请你说一说死锁发生的条件以及如何解决死锁6、系统调用和普通函数的区别7、操作系统中进程间通信方式8、操作系统中的PV指的是什么9、多进程和多线程的优缺点比较10、虚拟内存和物理内存怎么对应?11、操作系统的缺页异常12、vfork和fork13、谈谈并发与并行14、互斥锁、读写锁、自旋锁15、僵尸进程,孤儿进程16、系统调用的具体过程17、send和recv的底层过

xintu1314的博客 599

操作系统八股文合集

什么是操作系统 操作系统是管理计算机硬件和软件资源的计算机程序,提供一个计算机用户与计算机硬件系统之间的接口,向上对用户程序提供接口,向下接管硬件资源,操作系统本质上也是一个软件,作为最接近硬件的系统软件,负责处理器管理,存储器管理,设备管理,文件管理和提供用户接口 请介绍一下操作系统中的中断 中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保存现场后自动去执行相应的处理程序,处理完该事件后再返回中断处继续执行原来的程序。中断一般三类,一类是由CPU外部引起的,如I/O中断,时钟

weixin_45425093的博客 2380

2024年最全python进程间通信

添加数据函数print(“进程(%s)正在写入…”%(os.getpid()))print(“%s被写入到队列中”%(url))#读取数据函数print(“进程(%s)正在读取…”%(os.getpid()))print(“从队列中提取到:%s”%(url))if==“”:进程(21203)正在读取…进程(21201)正在写入…进程(21202)正在写入…ur1被写入到队列中从队列中提取到:ur1ur5被写入到队列中从队列中提取到:ur5ur2被写入到队列中。

2401_84691951的博客 132

Python渗透测试工具合集

http://www.freebuf.com/tools/94777.html Python渗透测试工具合集 网络 Scapy, Scapy3k: 发送,嗅探,分析和伪造网络数据包。可用作交互式包处理程序或单独作为一个库。 pypcap, Pcapy, pylibpcap: 几个不同 libpcap 捆绑的python库 libdnet: 低级网络路由,包括端口查看

wuxiaobingandbob的专栏 1225

秋招八股文合集基础篇

文章目录线程和进程:http 和https:tcp和udp:MySQL: 线程和进程: 总结:进程是资源分配的最小单位,线程是CPU调度的最小单位(进程和线程都是一个时间段的描述,是CPU工作时间段的描述。) 什么是进程:程序的一次执行过程。cpu会为进程开辟空间(CPU、内存)专门执行任务。 什么是线程:CPU上真正运行的是线程。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈) 区别:(1)进程拥有自己的资源空间,每启动一个进程,系统就会为它分

weixin_40372571的博客 408
上一篇: MFC 中的CList,CPtrList,CObList,CStringList有什么区别?
下一篇: 关于长按触摸笔[转]
皮鲁
博客等级 码龄23年 44粉丝 73原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值